🐰

Figmaのプラグインで、特定のノードを取得する方法

2024/06/07に公開

デザインエンジニアのりょーたです。

最近、「FigTodo」というFigmaのプラグインをリリースしました✨

https://www.figma.com/community/plugin/1376174862254820112

簡単にいうと、期限や担当者を設定できるタスク管理ツールなのですが、機能の1つに、アンカーリンクの作成があります。外部リンク(figmaファイル内の選択範囲へのリンクでない)であればブラウザでページを開き、ファイル内の特定のノードへのリンクであれば、そのノードの位置に移動します(下記参照)

ノードとは

Figmaにおけるノード(以降Nodeと記載します)とは、Figmaのドキュメント内の個々のオブジェクトや要素を表現するものであり、図形やテキスト、グループやセクションなど様々なオブジェクトに一意のIDが割り当てられています。

https://www.figma.com/plugin-docs/api/nodes/

この記事は、取得したNodeの位置に移動させる処理を実装するのに試行錯誤したため、その手順について共有します。
この記事で紹介するのは、デバッグをしつつ考えた方法です。(ドキュメントに似たユースケースが見つからず。。)
もし他に最適な方法がありましたらコメントいただけましたら幸いです。

Nodeの取得方法

まずは、Nodeの取得方法について紹介します。Nodeは、オブジェクトを選択した状態で右クリック > コピー/貼り付けオプション > 選択範囲へのリンクをコピーでコピーしたURL(下記参照)から取得することができます。

https://www.figma.com/design/[file_id]/[file_name]?node-id=1-1&t=KXy18Uv8TtP9i56C-4

Nodeは、URLクエリパラメータのnode_idの部分なので、上のURLでは「1-1」にがIDにあたります。

指定したNodeに移動する方法

ファイルの任意の位置から、特定のNodeのあるところに移動するには次の2つのステップが必要です(※1と2は順不同です)

  1. Nodeがあるページに移動する
  2. Nodeが画面に表示されるように移動する(Shift+1を押した際と同じ挙動)

Nodeがあるページに移動するには、setCurrentPageAsync関数を利用します。
Nodeのある位置に移動するには、scrollAndZoomIntoView関数を利用します。setCurrentPageAsyncscrollAndZoomIntoViewは、引数にPageNodeBaseNodeが必要なので、2つの関数を実行する前に、この情報を取得する必要があります。

PageNodeBaseNodeを取得するためには、今いるFigmaファイルの全てのページのNode情報を取得し、リンクから取得したNode IDと一致するNodeを見つけます。
ここで利用するのは、findChild関数です。

https://www.figma.com/plugin-docs/api/properties/nodes-findchild/

この関数は、特定のNodeの子Nodeを検索し、コールバックがtrueを返す最初のノードを返します。
今回のケースでは、ページ(PageNode)の子Nodeを検索し、最初に一致したNodeを取得します。

サンプルコードは以下の通りです。

        // NOTE: nodeIdはクエリパラメータのnode-idを指す
    for (const page of figma.root.children) {
      await page.loadAsync(); // ページの読み込み
      console.log(`Page ${page.name} has ${page.children.length} children`);
      // ページ内のノードを検索
      const node = page.findChild((node) => node.id === nodeId);
      // ノードが見つかったらページを切り替えてフォーカス
      if (node) {
        await figma.setCurrentPageAsync(page);
        figma.viewport.scrollAndZoomIntoView([node]);
        break;
      }
    }

「これでいける!」と思いきや、試してみると、nodeが見つからず終了してしまいます😭😭😭
そこで、PageNodeの子Nodeの配列をconsoleで確認すると、以下のように出力されました。

あれぇ.....リンクではnode-id=217-1552のようにハイフン区切りで取得されたのに、page.findChildによって取得されたNodeはコロン区切りになっています。
Node IDには、コロン以外の形式もあるようなのですが(調べてもNode IDについての公式のドキュメントが見当たらなかったので、Chat GPTによる見解です)リンクで取得しているNodeは

といったNodeに限定され、かつpage.findChildで確認した中で、コロン以外の形式がなかったため、今回の実装では、-(ハイフン):(コロン)に変換することにしました。

/**
 * ハイフン形式のnodeIdをコロン形式に変換する関数
 * @param {string} hyphenNodeId - ハイフン形式のnodeId (例: "1-2", "1-2-3")
 * @return {string} - コロン形式のnodeId (例: "1:2", "1:2:3")
 */
function convertNodeId(hyphenNodeId) {
    return hyphenNodeId.replace(/-/g, ':');
}

これで、Nodeがヒットするようになり、ページの切り替えとNodeが画面に表示されるようにビューポート座標を自動的に設定が行われます。

        // NOTE: nodeIdはクエリパラメータのnode-idを指す
+    const formatNodeId = convertNodeId(nodeId);
    for (const page of figma.root.children) {
      await page.loadAsync(); // ページの読み込み
      console.log(`Page ${page.name} has ${page.children.length} children`);
      // ページ内のノードを検索
+      const node = page.findChild((node) => node.id === formatNodeId);
      // ノードが見つかったらページを切り替えてフォーカス
      if (node) {
        await figma.setCurrentPageAsync(page);
        figma.viewport.scrollAndZoomIntoView([node]);
        break;
      }
    }

最後に

いかがでしたでしょうか?
Nodeについてのドキュメントは記述をもっとしてくれ!と思ったのですが、それでもドキュメント自体は丁寧に書かれているので、もしよければ皆さんもプラグインの作成にチャレンジしてみてください!

今回作成したプラグインについて、noteで作成に至った経緯やプラグインの機能についてまとめていますので、もしよければお読みいただければ嬉しいです!(いいねをいただけるともっと嬉しいです🫶)

https://note.com/ryotanny/n/n2ff842327a97

Discussion