この記事の概要
Googleスプレッドシートからデータを取得しFigmaに表を出力するプラグインを作成した
プラグインの内容を元に、Figmaの関数やnodeプロパティについての情報をまとめた
行いたい処理、それに必要な実際のコードとを記載している
前提
クロスドメインのためFigmaから直接GoogleAPIを呼び出すことはできない
そのためAPI GatewayからAPIを呼び出せるようにしている
Figmaプラグインを開発できる環境がある
作成したプラグインの概要
作成したプラグインのコード
- 行のレイアウトになる要素をFigmaで作成する
※ 列のテキスト要素のレイヤー名を#col
としておく
※ 列の要素数はスプレッドシートの数と同じにする - ブラグインを起動して作成した要素を選択
- スプレッドシートのIDを入力して
サービスアカウントからデータ取得
ボタンをクリック - 選択していた要素の下に表が出力される
プラグインで行っていること
- FigmaからAPIでデータを取得する
- 指定の名前のnodeを取得する
- nodeをcloneする
- nodeにテキストを追加する
コード解説
- ui.htmlでAPIを呼び出す
- ui.htmlからAPIで取得したデータをcode.tsで受け取る
- Figmaで選択中の要素を取得する
- 描画するnodeを準備する
- 準備したnodeを出力する
ui.html / code.ts コード
See the Pen Figma_Table_Creator by shiho (@shiho-hoshino) on CodePen.
1.ui.htmlでAPIを呼び出す
- 用意するもの
- API URI(AWS API Gateway)
- APIキー(AWS API Gateway)
以下ui.htmlのコード抜粋
クリックされたらXMLHttpRequestでスプレッドシートのデータを取得
取得できたらpostMessageでデータをcode.tsに送信する
<script>
const loader = document.getElementById('loader');
document.getElementById('getLocalApi').onclick = () => {
loader.classList.add('fadeIn');
const sheetId = document.getElementById('sheetId').value;
const range = document.getElementById('range').value || 'シート1';
const request = new XMLHttpRequest()
request.open('GET', `[API_URL]/${sheetId}/${range}`)
request.setRequestHeader('Content-Type', 'application/json');
request.setRequestHeader('x-api-key', '[APIキー]');
request.responseType = 'json'
request.onerror = function(e){
console.log(e);
alert('データが取得できませんでした');
loader.classList.remove('fadeIn');
};
request.onload = () => {
if (request.status === 200) {
parent.postMessage({ pluginMessage: {
type: 'setData',
response: request.response,
}}, '*')
}
loader.classList.remove('fadeIn');
};
request.send()
}
</script>
2.ui.htmlからAPIで取得したデータをcode.tsで受け取る
-
figma.showUI
モーダルのダイアログとしてHTMLを呼び出せる -
__html__
manifest.json ファイルの "ui" フィールドにファイル名を割り当てた場合、そのファイルの内容を取得できる -
figma.ui.onmessage
: uiフィールドのpostMessageを検知できる- 前述のui.htmlでMessageには
.type
と.response
オブジェクトを追加している-
.type
どのボタンのpostMessageか -
.response
APIの返り値
-
- 前述のui.htmlでMessageには
figma.showUI(__html__);
figma.ui.onmessage = async msg => {
if (msg.type === 'setData') {
/**
* Googleスプレッドシートから取得する情報 response.data.values
*/
const values = JSON.parse(JSON.stringify(msg.response.data.values));
3.Figmaで選択中の要素を取得する
-
currentPage
現在みているページのnodeを取得 -
currentPage.selection
選択中のnodeを取得- ここで選択されているのは
テーブルの行
のパーツになる要素1件である想定
- ここで選択されているのは
let selection: any = [];
const {currentPage} = figma;
const selectionNode = currentPage.selection;
if (selectionNode.length > 0) {
4.描画するnodeを準備する
選択している行要素のクローンして配列としてcloneNodes
変数に追加していく
-
figma.loadFontAsync
フォント情報を読み込む- これを先にやらないと要素にテキスト追加が行えないので完了をthenで検知しておく
-
SceneNode
FigmaのNode型定義 -
getColNodes
テキストを追加する列のnodeを取得する独自関数、詳細は後述 -
changeText
getColNodesで取得した要素のテキストを差し替える独自関数、詳細は後述
const cloneNodes: SceneNode[] = [];
figma.loadFontAsync({
"family": "Helvetica",
"style": "Regular"
}).then(() => {
selectionNode.map((node, index) => {
// スプレッドシートの行ごとに処理
values.map((value, i) => {
// nodeをcloneし配列に追加
cloneNodes.push(node.clone())
// cloneしたnodeからテキストを反映するnodeを取得
const colNodes = getColNodes(cloneNodes[i]);
// 指定要素にテキストを反映
colNodes.map((node, i) => {
const text = value[i] || '';
changeText(node, text);
});
});
})
setText
- children: 子要素が含まれるとchildrenオブジェクトが存在する
- 子要素も含め対象のnodeの中から「#cal」のnameを持つ要素を取得する
/**
* @param obj 行
* @param nodes 最終的なreturn値
*/
const getColNodes = function (obj, nodes = []) {
if (Array.isArray(obj)) {
obj.map((node: any, index) => {
if (node.name.indexOf("#col") != -1) {
nodes.push(node);
} else if (node.children) {
getColNodes(node.children, nodes);
}
});
} else {
if (obj.name.indexOf("#col") != -1) {
nodes.push(obj);
} else if (obj.children) {
getColNodes(obj.children, nodes);
}
}
return nodes;
};
setText
- .characters: nodeがテキストの場合charactersオブジェクトが存在する
- deleteCharacters: nodeのテキストを削除する
- insertCharacters: nodeに指定場所からテキストを追加する
/**
* @param node テキストを反映するNode
* @param text 変更するテキスト
*/
const changeText = function (node, text) {
const obj: any = {};
if (node.characters) {
try {
node.deleteCharacters(0, node.characters.length);
node.insertCharacters(0, text);
} catch (e) {
console.log(e);
}
}
};
5.準備したnodeを出力する
-
createFrame
フレームを作成する -
.x
選択中の要素のx軸の数値を取得 -
.y
選択中の要素のy軸の数値を取得 -
.height
選択中の要素の高さを取得 -
insertChild
nodeに追加(フレームとブロックnodeにのみ可能) -
layoutMode
VERTICALで、Y軸基準にする -
primaryAxisSizingMode
layoutModeで設定した軸のサイズのモードを変更する
サイズの固定か自動を選べる、autoで中のテキストサイズに合わせてY軸(VERTICAL)可変になる -
counterAxisSizingMode
layoutModeで設定した軸とは逆の軸のサイズモードを変更する
// フレーム作成
const frame = figma.createFrame();
figma.currentPage.selection = [frame];
// フレームの位置を設定
const x = selectionNode[0].x;
const y = selectionNode[0].y + selectionNode[0].height + 20;
frame.x = x;
frame.y = y;
// Auto layout を有効にする
frame.layoutMode = "VERTICAL";
// フレームに作成した行Nodeを追加
cloneNodes.map((node, index) => {
frame.insertChild(index, node);
});
// コンテンツサイズで自動リサイズするモードに変更
frame.primaryAxisSizingMode = "AUTO";
frame.counterAxisSizingMode = "AUTO";
以上
まとめ
Figmaのプラグイン開発をスムーズに行うにはNode構造の把握が不可欠だった
注意点
Figmaは Figma design と FigJam の二種類のツールがあり
Figmaのドキュメントには二つのツールのNodeプロパティについての解説が混在している
Figma designで使えると思った関数がFigJamのもので使えないことなどもあったので
どちらのものなのか都度確認した方が良い