[Figma Plugin API] オブジェクトを一括変更するプラグインを作る！

# はじめに
watnow アドベントカレンダー2024 16日目は、Figma の Plugin API を使ってみたら思ったよりも簡単で面白かったのでオリジナルのプラグインを作ってみたよ、という記事です📝


# 今回作ったもの
オブジェクト置き換えくん

Figmaファイルの中にある同じ名前のオブジェクトを、指定した別のオブジェクトに一括で変更することができるプラグインです。実行のイメージはこんな感じ ↓

![スクリーンショット 2024-12-15 1.27.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303938/00982126-a25d-b755-a0bc-92a01ea62882.png)

それでは早速作っていきます！


# プラグイン作成
## 準備
### Figma デスクトップアプリを用意する
Figmaのプラグインの作成はデスクトップアプリから可能です。以下リンクからお使いの環境に合わせてダウンロードしてください。

https://www.figma.com/ja-jp/downloads/

### エディターを用意する
特にエディターの指定はないので使いやすいツールを使ってください。ここでは Visual Studio Code を使用しています。

https://code.visualstudio.com/

### Node.js を用意する
プラグインのスクリプトは TypeScript で作成します。( Figma 推奨）TypeScript のプログラムをビルドするためには Node.js が使える環境を用意する必要があります。以下リンクからお使いの環境に合わせてダウンロードしてください。

https://nodejs.org/en/download/package-manager

ダウンロードできたかどうかは、ターミナルから以下のコマンドで確認することができます。

```terminal.
node -v
```


## 新しいプラグインの作成
### デザインファイルを作成する
Figmaのデスクトップアプリを立ち上げ、デザインファイル新規作成を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303938/788f2d89-5bfb-ce20-5578-0b43e479895e.png)

### プラグインの新規作成
デザインファイル下部のツールバーからリソースをクリックし、プラグインとウィジェットを選択します。「プラグインの新規作成...」という項目があるのでここから始めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303938/bc0f5dbb-2126-c82d-e2d9-f6cbf7cf03d4.png)

クリックすると以下のようなモーダルが出てくるので、今回は「Figmaデザイン」→「カスタムUI」を選択します。名前の欄に入力したものがローカルに作成されるフォルダ名になるので、アルファベットで自分のわかりやすい名前にすると良いと思います。名前を付けて保存しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303938/df04a16b-77eb-e984-6732-4a08bef820fd.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303938/0bf12e66-7e53-083d-a3d7-82393c972d00.png)


## プログラムの作成
### プロジェクトファイルをエディターから開く
先ほど作成した新しいプラグインのフォルダを VSCode で開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303938/b52ec0ed-6487-17ea-d5f1-fa2f71716246.png)

### 依存関係をインストールする
まず、Figmaプラグイン開発に必要な依存関係をインストールします。新しいターミナルを開いて以下のコマンドを入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303938/4d2aa472-578e-b754-239e-71d1ce8eaeb6.png)

```terminal.
npm install
```

### ビルドする
プロジェクトを作成したときに作られるフォルダの中には、入力した数だけオブジェクトを新規作成するプラグインのスクリプトがすでに用意されています。ビルドして実行してみましょう。Mac の方は Command-Shift-B、Windows の方は Ctrl-Shift-B を押してください。npm: build を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303938/1055ac2d-53ce-33f3-726e-045d3187d2b8.png)

### プラグインを実行する
Figma に戻り、ツールバーのリソースから、先ほど作ったプラグインを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303938/59b6541a-8480-6da5-1ee4-39642b4bcf0d.png)

選択すると以下のようなパネルが開き、実行すると5つのオブジェクトが新規作成されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303938/5cf8c43c-ae70-9dce-cf0b-a9a9ba580932.png)

### オブジェクトを一括変更するプログラムにする
今回プログラムの作成は ChatGPT にやってもらいました。code.ts と ui.html を以下のように書き換えます。

```code.ts
/// <reference types="@figma/plugin-typings" />

figma.showUI(__html__, { width: 300, height: 200 });

figma.ui.onmessage = async (msg: { type: string; targetName: string; replacementName: string }) => {
  if (msg.type === 'replace') {
    const { targetName, replacementName } = msg;

    if (!targetName || !replacementName) {
      figma.notify("オブジェクト名を両方入力してください");
      return;
    }

    const allNodes = figma.currentPage.findAll();
    const targetNodes = allNodes.filter((node: SceneNode) => node.name === targetName);
    const replacementNode = allNodes.find((node: SceneNode) => node.name === replacementName);

    if (!replacementNode) {
      figma.notify("置き換え後のオブジェクトが見つかりませんでした");
      return;
    }

    let count = 0;
    for (const node of targetNodes) {
      if ('x' in node && 'y' in node) {
        const newNode = (replacementNode as SceneNode).clone();

        // 元の親ノードを取得
        const parent = node.parent;
        if (parent && 'appendChild' in parent) {
          parent.appendChild(newNode); // 親ノード（フレーム）に追加
        }

        // 元の位置を引き継ぐ
        newNode.x = node.x;
        newNode.y = node.y;

        // 元の相対位置（フレーム内の座標）を維持するために設定
        if ('relativeTransform' in node) {
          newNode.relativeTransform = node.relativeTransform;
        }

        // 元のオブジェクトを削除
        node.remove();
        count++;
      }
    }

    figma.notify(`${count}個のオブジェクトを置き換えました`);
    figma.closePlugin();
  }
};
```

```ui.html
<!DOCTYPE html>
<html>
  <head>
    <style>
      body {
        font-family: Arial, sans-serif;
        padding: 16px;
        max-width: 300px;
      }
      input,
      button {
        width: 100%;
        margin-bottom: 8px;
      }
    </style>
  </head>
  <body>
    <label for="targetName">置き換え前のオブジェクト名:</label>
    <input type="text" id="targetName" placeholder="OldObject" />

    <label for="replacementName">置き換え後のオブジェクト名:</label>
    <input type="text" id="replacementName" placeholder="NewObject" />

    <button id="submit">実行</button>

    <script>
      const submitButton = document.getElementById("submit");
      submitButton.onclick = () => {
        const targetName = document.getElementById("targetName").value;
        const replacementName =
          document.getElementById("replacementName").value;

        parent.postMessage(
          { pluginMessage: { type: "replace", targetName, replacementName } },
          "*"
        );
      };
    </script>
  </body>
</html>
```

前述の手順で再度ビルドします。Figma に戻り、プラグインを選択すると以下のようなパネルが表示されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303938/961f4395-26fa-798c-1c3b-c02d4365d42f.png)

オブジェクトの名前を入力して実行すると...

![画面収録 2024-12-16 22.27.13 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303938/650fdb6d-2026-cb34-d2fc-5c8b72d1067a.gif)

望み通りのプラグインができましたー！！！🎉


# おわりに
Figma のプラグイン、思っていたよりも簡単に作成できました。Figma の API には他にも以下のような種類と特徴があります。

|APIの種類|主な用途|使用タイミング|
|:---:|:---:|:---:|
|REST API|デザインデータの取得・操作、外部連携|他のサービスとデータをやり取りする場合|
|Plugin API|Figma内部のカスタマイズ、自動化|デザイン作業を効率化したい場合、独自のツールを作成する場合|
|Webhook API|イベント通知と連携、自動化|リアルタイムでイベントを検知して外部サービスに通知したい場合|

目的に合わせた API 利用でもっともっと便利に Figma を使いこなせると良いですね！

2024年ももうあと少し、みなさま良いお年を〜☃️

# 参考
+ https://www.figma.com/plugin-docs/
+ https://qiita.com/degudegu2510/items/929b54959f5917f2b218#file-manifestjson