0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Figmaに表を作り出すプラグインの作成

Last updated at Posted at 2022-07-03

この記事の概要

Googleスプレッドシートからデータを取得しFigmaに表を出力するプラグインを作成した
プラグインの内容を元に、Figmaの関数やnodeプロパティについての情報をまとめた
行いたい処理、それに必要な実際のコードとを記載している

前提

クロスドメインのためFigmaから直接GoogleAPIを呼び出すことはできない
そのためAPI GatewayからAPIを呼び出せるようにしている

Figmaプラグインを開発できる環境がある

作成したプラグインの概要

作成したプラグインのコード

  1. 行のレイアウトになる要素をFigmaで作成する
    ※ 列のテキスト要素のレイヤー名を#colとしておく
    ※ 列の要素数はスプレッドシートの数と同じにする
  2. ブラグインを起動して作成した要素を選択
  3. スプレッドシートのIDを入力してサービスアカウントからデータ取得ボタンをクリック
  4. 選択していた要素の下に表が出力される

figma1.png

プラグインで行っていること

  • FigmaからAPIでデータを取得する
  • 指定の名前のnodeを取得する
  • nodeをcloneする
  • nodeにテキストを追加する

コード解説

  1. ui.htmlでAPIを呼び出す
  2. ui.htmlからAPIで取得したデータをcode.tsで受け取る
  3. Figmaで選択中の要素を取得する
  4. 描画するnodeを準備する
  5. 準備した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__);
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

/**
 * @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のもので使えないことなどもあったので
どちらのものなのか都度確認した方が良い

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?