• Figma
  • 開発

FigmaのPlugin(プラグイン)の開発方法/作り方

最終更新日時
Figmaは、2019年8月にプラグイン機能をリリースしました。

ユーザーは自作のプラグインをFigma上で公開することができます。2019年10月現在、約200のプラグインがリリースされています。

先日、筆者もFigmaのPluginをPublishを行いました。 そこで得た知見を元に、プラグイン作成の要点を紹介していきます。

この記事のゴール

「Figmaのプラグインの作り方」と題していますが、この記事では詳細なHowToを伝えるというよりは、プラグインの作り方の要点を把握でき、それにより設計をイメージすることができるようになることを目指しています。

また、この記事は長いので、ザーッとスライドしながら黄色い画像だけみていけばなんとなくわかるように作っています。ザラみで読み進めていただければと思います。

Figmaプラグイン理解の5つのステップ

解説を5つのステップに分けてみました。この5つを理解すれば、Figmaのプラグインの勘所がおおよそつかめますので、自分の作りたいFIgmaのプラグインの設計を描きやすくなります。特に②・③・④さえ理解してしまえば、あとはほぼ普通のフロントエンドアプリケーションの開発と同じです。

それでは始めていきましょう。

①まずはサンプルコードを試して実感してみる。

まずはサンプルを作成し、雰囲気を掴んでみましょう。サンプルコードの作成はすぐにできます。写経などの必要はありません。なおプラグインの開発には、アプリ版のFigmaが必要になります。

また、Typescriptnode(yarn)を開発に使用します。またTypescriptを使用するのでVscodeの使用が強く推奨されています。これらをインストールしていない方は検索して事前のインストールをお願いいたします。この記事では、こちらの開発環境のセットアップなどは省いています。

①-1 サンプルコードを生成する

サンプルコードをfigmaがジェネレートしてくれます。写経などが必要ありませんのですぐに始めることができます。以下の4つのステップで開始できます。

①-2 ターミナルでの作業

①-1で生成したフォルダに移動しvscodeを開きます。(あるいはvscodeで①-1で生成したフォルダを開きます)

その後vscode上でターミナルを開き、npmのパッケージをインストールしましょう。
$ npm install

①-3 ビルドおよび起動

Windowsの人は「Ctrl+shift+B」、Macの人は「 ⌘+shift+B」をおして、Typescriptをビルドします。押すと下記の画面がでるので、tsc:watchを選びましょう。

①-4 Figmaに戻ってプラグインを起動

右上のメニューのPluginから「Development」を選び、Sampleを選びましょう。

①-5 サンプルコードを試してみましょう。

サンプルのプラグインがポップアップで出現しますので、早速Createを押して試してみましょう。
Createを押すと..
こんな感じで四角形がジェネレートされます。

①-6 ここで理解したいポイント

プラグインはTypescriptをビルドして作るものである。
厳密にはJavascriptでも書けますが、FigmaがTypescriptを強く推奨しています。 またTypescriptを扱うゆえ、Vscodeの使用を強く推奨します。
UIとCodeという二つの概念が存在する。
Typescriptでビルドされた、code.jsとui.htmlというファイルに注目してみましょう。 code.jsは、figma上で起動するいわばプラグインの本体となります。 ui.htmlは、ポップアップで出現するUIとなります。この二つの構造を理解することがプラグイン作成理解のコツです。この二つの構造について次の章で説明します。

それではサンプルのジェネレートができ、プラグインはまずはこういうものだということがわかったかと思います。それでは、本格的に中身を理解していきましょう。

②「UI」と「CODE」を理解する。

code.jsとui.htmlの2つがあることをまず理解しましょう。

code.jsはfigmaプラグインのいわば「本体」部分で、figmaを直接操作することが許可されている部分です。 ui.htmlは、通常のフロントエンドと同じようにUIを作ることができますが、figmaを直接操作することができません。

code.jsがfigmaに対してできることは多岐に及びます。生成されたサンプルにもあるような「図形のジェネレート」や「選んでいるものの取得」、あるいは「全オブジェクトを検索する」といったことができます。なお、具体的なインターフェース(できること一覧)は、サンプルをジェネレートした際に作られた「figma.d.ts」に記載されています。ぜひご覧になってみてください。

先ほどui.htmlは、figmaを直接操作することができない、と説明しました。しかし、サンプルで生成されるユーザーの操作を受け付け、レクタングルをfigma上に生成しています。見た所、直接uiからfigmaに操作支持を出しているように思えます。一体どういうことなのでしょうか。

これはWebMessagingAPIを利用し、ui.htmlからcode.jsに、命令を送信することで実現されています。Messaging APIはFigmaの独自の機能ではなく、現在のブラウザであれば標準的に備わっているブラウザが持つ標準機能です。ことなるブラウザのウインドウ(タブ)間でデータを通信するために利用されます。Figma自体がWebで動いているので、ui.htmlとFigmaは、それぞれブラウザで開かれた別ウインドウに相当するものになっています。そのためMessagingAPIなど、通常Webで使われる仕組みが、このように利用されているというわけですね。

では次の図でMessageAPIを使った、ui.htmlからfigmaを操作するフローを見ていきましょう。

②-1 ui.htmlから、figmaへの操作を実行する

ui.htmlからfigmaの操作を実行するトリックは、上の図のような流れとなります。

まずui.htmlからcode.jsにMessageAPIを介して、メッセージを送信します。code.jsは受け取ったメッセージをif文やswitch文などによって判別し、行いたい動作をfigmaに要求する、といった流れです。

②-2 ui.htmlで、figmaの状態を表示する

ui.htmlはfigmaへの操作を直接できないだけでなく、「参照」もできません。ui.htmlでfigmaの状態を表示したい場合、②-1の手順とは逆で、code.jsからMessagingAPIのMessageを送信し、ui.html側でメッセージをif分やswitch文などでハンドリングします。

②-3 ui.htmI / code.js の整理

ここまでの情報を整理しましょう。できることとできないことを、一覧にしました。
ui.htmlとcode.jsという二つのファイルがあるという事と、それぞれの役割の違いを理解いただけたでしょうか。次の項目からは、具体的なコードの説明に入っていきます。

③UIからCodeへの操作を理解する

それでは具体的なコードでUIからCodeの操作を見ていきましょう。

③-1: code.tsを読み解く

サンプルでジェネレートしたcode.tsを見ていきましょう。code.tsには、UI.htmlのlaunch、およびMessageを受信した際のハンドリングが定義されています。
code.ts
// This shows the HTML page in "ui.html".
figma.showUI(__html__);

// Calls to "parent.postMessage" from within the HTML page will trigger this
// callback. The callback will be passed the "pluginMessage" property of the
// posted message.
figma.ui.onmessage = msg => {
  // One way of distinguishing between different types of messages sent from
  // your HTML page is to use an object with a "type" property like this.
  if (msg.type === 'create-rectangles') {
    const nodes: SceneNode[] = [];
    for (let i = 0; i < msg.count; i++) {
      const rect = figma.createRectangle();
      rect.x = i * 150;
      rect.fills = [{type: 'SOLID', color: {r: 1, g: 0.5, b: 0}}];
      figma.currentPage.appendChild(rect);
      nodes.push(rect);
    }
    figma.currentPage.selection = nodes;
    figma.viewport.scrollAndZoomIntoView(nodes);
  }

  // Make sure to close the plugin when you're done. Otherwise the plugin will
  // keep running, which shows the cancel button at the bottom of the screen.
  figma.closePlugin();
};
ui.htmlの立ち上げ
まず冒頭に存在するこの2行に注目してみます。
code.ts
// This shows the HTML page in "ui.html".
figma.showUI(__html__);
これは、ui.htmlを起動する操作となります。この記述をしない場合、ui.htmlは立ち上がりません。ui.htmlの起動もcode.jsによって行われます。

ここで注目すべきは起動順です。ユーザーはFigmaアプリの「Plugin」タブよりプラグインを起動します。プラグインを起動すると、まずcode.jsが走ります。そしてcode.jsの中で、ui.htmlの立ち上がりが記述されている場合、code.jsがui.htmlを立ち上げる、といった順番になっています。
MessagingAPI受信時のコールバック登録
code.ts
// Calls to "parent.postMessage" from within the HTML page will trigger this
// callback. The callback will be passed the "pluginMessage" property of the
// posted message.
figma.ui.onmessage = msg => {
次はこの箇所です。figma.ui.onmessageにて、メッセージを受信した時のアクションを定義することができます。msgには受信したmessageのbodyが含まれます。
code.ts
if (msg.type === 'create-rectangles') {
    const nodes: SceneNode[] = [];
    for (let i = 0; i < msg.count; i++) {
      const rect = figma.createRectangle();
      rect.x = i * 150;
      rect.fills = [{type: 'SOLID', color: {r: 1, g: 0.5, b: 0}}];
      figma.currentPage.appendChild(rect);
      nodes.push(rect);
    }
    figma.currentPage.selection = nodes;
    figma.viewport.scrollAndZoomIntoView(nodes);
  }
この箇所はmsgの中身を判別し、msgの中身に応じて処理を分岐させる処理です。

この場合は、msgというオブジェクトのtypecreate-rectanclesである場合、ブロック内部の処理を実行するというコードとなります。
msgは、javascriptのobjectです。ui.htmlから自由なobjectを送信することができます。
プラグインのクローズ処理
code.ts
 figma.closePlugin();
この記述はプラグインのクローズ処理です。code.jsの処理を終了し、ui.htmlを閉じるという処理となっています。

③-2: ui.htmlを読み解く

次に、サンプルでジェネレートされたui.htmlを見ていきましょう。ui.htmlには、htmlで構成されたパーツだけでなく、javascriptコードも含まれています。
ui.html
<h2>Rectangle Creator</h2>
<p>Count: <input id="count" value="5"></p>
<button id="create">Create</button>
<button id="cancel">Cancel</button>
<script>

document.getElementById('create').onclick = () => {
  const textbox = document.getElementById('count');
  const count = parseInt(textbox.value, 10);
  parent.postMessage({ pluginMessage: { type: 'create-rectangles', count } }, '*')
}

document.getElementById('cancel').onclick = () => {
  parent.postMessage({ pluginMessage: { type: 'cancel' } }, '*')
}

</script>
WebMessaging送信部分に注目
ui.html
document.getElementById('create').onclick = () => {
  const textbox = document.getElementById('count');
  const count = parseInt(textbox.value, 10);
  parent.postMessage({ pluginMessage: { type: 'create-rectangles', count } }, '*')
}
この箇所こそがcode.jsにメッセージを送信している箇所です。特に4行目を見てください。
ui.html
parent.postMessage({ pluginMessage: { type: 'create-rectangles', count } }, '*')
parentというのは、この場合「Window」を指しています。Windowが持つ「postMessage」関数で、「{ pluginMessage: { type: 'create-rectangles', count } }」というメッセージをcode.jsに送信します。
③-3 ui.htmlからcode.jsへのデータフローを理解する。
ここまででの解説でcode.jsとui.htmlの中身が見えてきたと思います。一旦プラグインの軌道からユーザーのアクションから実行までの流れを図で整理して理解してみましょう。
プラグインを起動すると、まずcode.jsが立ち上がります。figma.showUIにより起動がuiが起動します。

その後ユーザーがui.htmlでアクションを実行すると、messagingAPIを介してcode.jsにメッセージが送信されます。そのメッセージを判別し、適切な処理をfigmaに実行します。

上記がui.htmlからcode.jsにデータを送信する流れです。次は逆の流れをみてみましょう。

④CodeからUIへの操作を理解する

code.jsからui.htmlにデータ送信する逆の流れをやってみましょう。これはサンプルコードにありませんので、サンプルに新しいコードを追加する形で進めていきます。

完成したサンプルのイメージ

選択中のオブジェクトをcode.tsが読み取り、それらオブジェクトの名前と横幅をui.htmlに送信するというサンプルです。
改変した新しいcode.ts
code.ts(code.ts)では大きく二つの改変箇所があります。
code.ts
figma.showUI(__html__);

figma.ui.onmessage = msg => {
  // ②追加したコードです
  if (msg.type === "get-elems") {
    const nodes = figma.currentPage.selection;
    figma.ui.postMessage({
      type: "get-elems",
      data: nodes.map(item => {
        return { name: item.name, width: item.width };
      })
    });
  }

  if (msg.type === "create-rectangles") {
    const nodes: SceneNode[] = [];
    for (let i = 0; i < msg.count; i++) {
      const rect = figma.createRectangle();
      rect.x = i * 150;
      rect.fills = [{ type: "SOLID", color: { r: 1, g: 0.5, b: 0 } }];
      figma.currentPage.appendChild(rect);
      nodes.push(rect);
    }
    figma.currentPage.selection = nodes;
    figma.viewport.scrollAndZoomIntoView(nodes);
  }

  // ①コメントアウトします。
  // figma.closePlugin();
};
(不要な英文のコメントは削除しています。)
改変した新しいui.html
ui.htmlの改変箇所は3つです。
ui.html
<h2>Rectangle Creator</h2>
<p>Count: <input id="count" value="5" /></p>
<!--③追加したコードです-->
<div>
  GetElements:
  <p id="elems"></p>
</div>
<button id="create">Create</button>
<button id="get">Get Elements</button>
<button id="cancel">Cancel</button>
<script>
  /* ④追加したコードです */
  document.getElementById("get").onclick = () => {
    parent.postMessage({ pluginMessage: { type: "get-elems" } }, "*");
  };

  document.getElementById("create").onclick = () => {
    const textbox = document.getElementById("count");
    const count = parseInt(textbox.value, 10);
    parent.postMessage(
      { pluginMessage: { type: "create-rectangles", count } },
      "*"
    );
  };

  document.getElementById("cancel").onclick = () => {
    parent.postMessage({ pluginMessage: { type: "cancel" } }, "*");
  };

  /* ⑤追加したコードです */
  onmessage = message => {
    if (message.data.pluginMessage.type == "get-elems") {
      let params = "";
      message.data.pluginMessage.data.map(item => {
        params = params + `${item.name} | width: ${item.width} <br/>`;
      });

      document.getElementById("elems").innerHTML = params;
    }
  };
</script>

④-1 code.tsの改変箇所を説明

figma.closePluginはコメントアウト
code.ts
// ①コメントアウトします。
// figma.closePlugin();
まずはここは邪魔なので削除します。
figma.onmessageに新しいイベントハンドリングを追加し、ui.htmlにメッセージを送信する箇所を記述する
code.ts
figma.ui.onmessage = msg => {
  // ②追加したコードです
  if (msg.type === "get-elems") {
    const nodes = figma.currentPage.selection;
    figma.ui.postMessage({
      type: "get-elems",
      data: nodes.map(item => {
        return { name: item.name, width: item.width };
      })
    });
  }
...
コールバックに、ui.htmlからのメッセージハンドリングを新しく追加します。

code.jsのイベント発火タイミングの一つである「onmessage」にイベントを追加します。uiから一旦メッセージを発信し、code.jsが受信してui.htmlにmessageを送るという実装をしています。

この記述は、ui.htmlから{type: 'get-elems', ...}というメッセージを受け取った場合、現在、figmaで選択しているオブジェクトの一覧を取得し、その取得したオブジェクトをui.htmlにMessageAPIで送信する、という記述をしたものとなっています。

もう少し、この箇所のコードを細かくみていきましょう。
現在の選択しているオブジェクトを取得する
code.ts
const nodes = figma.currentPage.selection;
上記の記述はfigmaの現在の選択しているオブジェクトを配列で取得する処理です。
Figma.ui.postMessageでui.htmlにメッセージを送信する
code.ts
  figma.ui.postMessage({
      type: "get-elems",
      data: nodes.map(item => {
        return { name: item.name, width: item.width };
      })
    });
ここはui.htmlにメッセージを送信する部分です。ここで一点重要なのが、code.tsではWebAPIのwindow.postMessageは使わずに、figma.ui.postMessageを利用してMessageを送信している点です。

code.jsではWebAPIを利用することができません。他にもlocalstroageなどブラウザで当たり前のように利用できているものが利用できません。

その代わりfigmaオブジェクトがWebAPIのいくつかの機能をWrapする形で提供しています。今回のfigma.ui.postMessageはその一つです。

では次はui.htmlの改変箇所をみていきましょう。

④-2 ui.htmlの改変箇所

情報表示スペース/イベント発火のボタンを作成
ui.html
<!--③追加したコードです-->
<div>
  GetElements:
  <p id="elems"></p>
</div>
<button id="get">Get Elements</button>
GelElement: 〜という記述は、code.jsで受け取った情報を表示するスペースです。

GetElementsボタンは、code.jsにメッセージを送る処理を発火させるボタンです。
メッセージを新しく送信する処理を記述
ui.html
  /* ④追加したコードです */
  document.getElementById("get").onclick = () => {
    parent.postMessage({ pluginMessage: { type: "get-elems" } }, "*");
  };
GetElementsボタンを押下した際に発火するイベントです。{ pluginMessage: { type: "get-elems" } }というメッセージがcode.jsに送信されます。
code.jsからui.htmlを受け取る処理を記述
ui.html
 /* ⑤追加したコードです */
  onmessage = message => {
    if (message.data.pluginMessage.type == "get-elems") {
      let params = "";
      message.data.pluginMessage.data.map(item => {
        params = params + `${item.name} | width: ${item.width} <br/>`;
      });

      document.getElementById("elems").innerHTML = params;
    }
  };
ここがcode.jsからメッセージを受け取った際に、データをhtmlに展開する処理を記述した箇所です。

code.jsとは違い、ui.htmlはWebAPIを使用できますので、onmessageでmessageのコールバックを登録します。

code.jsから受信したメッセージが{type:"get-elems"..}という場合のみ、データをid="elems"の記述のあるDOM(htmlの部品のこと、ここでいう<p id="elems">)に展開します。

これにてcode.jsのデータを取得して表示するという流れが記述完了となります。

それでは長くなりましたので、code.jsからui.htmlを受け取る処理の流れを次の項目で図で整理していきます。

④-3 code.jsからui.htmlに表示するフローを理解する。

code.jsは直接イベントトリガーを引けないので、まずはui.htmlからメッセージを送信して発火させます。点線で囲った箇所がその流れです。

メッセージがui.htmlから送られてきたら、code.jsでそのメッセージを受け止め、figmaのデータの取得を行います。取得完了後、すぐさまcode.jsはui.htmlにメッセージを送ります。

ui.htmlがメッセージを受け取ったら、そのデータをuiに反映します。

以上がcode.jsからui.htmlにメッセージを送信する流れとなります。
ここまでで..
これらの概念を掴んでしまえば、あとの実装は簡単です。ui.htmlにてフロントエンドをどう進めていくか。code.jsでfigmaに対してどんな操作をしていくか。この2点を設計し、通常通りのWeb開発を行うのみです。

ui.htmlを作り込みたい場合は、reactやvueを使うのがおすすめです。reactやwebpackの初期設定はこちらに書いてあります。(vueは載っていなかったので自力でやりましょう)
またcode.jsで実装するfigmaに関する操作は、figma公式のplugin-docsや、サンプルジェネレート時の型定義ファイルfigma.d.tsを見ると良いでしょう。

それでは次に公開の手順を説明します。

⑤公開までの流れを確認する。

Pluginが完成したらPublishしましょう。FigmaをFigma Organizationで契約している場合は、参加メンバー間でPrivateにリリースすることもできるようです。そちらはやったことがないので、今回は通常のPublishを行うものとします。

⑤-1 Publishの方法

⑤-2 審査を待ちます。

2019年10月現在審査には5〜10営業日かかるようですが、同時期、筆者が申請したとときは2日程度でした。 また審査に関してガイドラインが定められています。Plugin Review Guidelinesを確認しましょう。安全性が担保されているか、ユーザビリティが担保されているかなど当たり前の項目が並んでいます。短い文章なので一度目を通しておきましょう。プラグインのマネタイズは許可されているようです。一方、広告によるマネタイズはNGとのことでした。

⑤-3 公開

公開の際にはメールが届きます。丁寧なフィードバックも返していただけるので嬉しい気持ちになります。また公式のガイドラインによると、rejectの際にもメールでお知らせするとのことでした。
これにて以上となります。

最後に自作のプラグインの紹介

Figma Code Highlighterというプラグインを作成いたしました。コードをIDEのようにハイライトできるツールです。

Figmaでコードの解説資料を作る機会があり、コードをいちいち手でハイライトするのは面倒になってきたので、いっそのことハイライトツールを作ろうとなりまして、今回作成に至りました。

Figmaはデザインツールでありますが、実は資料作成にも便利で、パワポやエクセルの代わりに活用しています。(実はそっちの使用頻度の方が多い)パワポやエクセルと違ってリアルタイムに共同編集でき、Web上で完結するのが魅力です。リモートでのコラボレーションにはもはや欠かせないツールとなっています。PDFの書き出しもすぐできるので、実はFigmaはLTの資料作成にもちょうどよいツールとなっています。 しかもチームで添削できるし効率的です。

(特にビジネス職の方に使っていただきたいと思って、いつも布教しています。結構すぐに便利さを理解していただいて、パワポをやめてFigmaをバキバキ活用し始めるので、きっとマッチしているんだと思います。ビジネス職・エンジニア職・デザイン職が垣根なくこのプロダクトを使うことで、提案デザインの書き出しだとか、あるいはパーツの書き出しどうするんだとか、そういう不毛なコミュニケーションが全面的に消えるのでおすすめです。)

話がそれました。こちらFigma Code Highligher、内部的にはhighlight.jsのwrapperになっています。そのためhighlight.jsで使用されているカラースキーマと対応言語をそのまま使用できるようになっています。
ソースコードはこちらです
こちらにソースコードを公開しています。よろしければご査収ください。
https://github.com/ixtgorilla/figma-code-highlighter
書き手
IXTゴリラ
暗号通貨の情報の図解やスライドの配信を行なっているゴリラ。