从今天开始开个新的专栏,从零开始开发一个 figma design to code 插件。
阅读并且实操完本系列文章,你会
- 对 figma 设计工具有深入的理解,甚至可能会比你们公司 ui 同学理解的还要深刻
- 熟悉代码生成原理,自己也能够写出一个效果不错的插件
- 对页面布局有了新的认知(比如不用 margin),大大提升你写 html 的速度和还原度
开发 figma 插件需要使用 figma 客户端。我们打开 figma 客户端,新建设计文件,然后点击顶部栏的 Plugins
-> Development
-> New Plugin
然后选择
Figma design
,输入插件名字,点击确定
选择 Custom UI
将代码保存到本地,用你的编辑器打开,然后跟随readme 的指引运行项目
css
npm install -g typescript
npm install --save-dev @figma/plugin-typings
npm run watch
这样项目就跑起来了
之后打开 figma 客户端运行你的插件
点击 create 之后会生成五个黄色的矩形
看下代码目录结构,我就说几个重要
manifest.json
是插件描述文件,里面包含插件的 name,id,ui和 mian,ui 就是弹窗里面展示的 ui。main 是运行在沙盒环境的代码,里面应该是你主要的业务逻辑。他们通过 postMessage 通信
json
{
"name": "my-first-plugin",
"id": "1482330158480649678",
"api": "1.0.0",
"main": "code.js",
"capabilities": [],
"enableProposedApi": false,
"documentAccess": "dynamic-page",
"editorType": [
"figma"
],
"ui": "ui.html",
"networkAccess": {
"allowedDomains": [
"none"
]
}
}
ui.html
html
<h2>Rectangle Creator</h2>
<p>Count: <input id="count" type="number" 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-shapes', count } }, '*')
}
document.getElementById('cancel').onclick = () => {
parent.postMessage({ pluginMessage: { type: 'cancel' } }, '*')
}
</script>
code.ts
ts
figma.showUI(__html__);
figma.ui.onmessage = (msg: {type: string, count: number}) => {
if (msg.type === 'create-shapes') {
const numberOfRectangles = msg.count;
const nodes: SceneNode[] = [];
for (let i = 0; i < numberOfRectangles; 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();
};
看完代码现在就产生了一个问题,就是他们提供的代码实在是太古老了,过于刀耕火种了🔪⛏️🔥🌱,所以我来基于 react、vite 、ts 改造一下。
大概分享一下我的改造过程
-
在根目录直接npm create vite@latest
-
再给文件移出来🐶
-
创建 vite.ui.config.ts,用于build react 代码,使用viteSingleFile插件把代码都打包到 html 中
-
创建 vite.plugin.config.ts 用于 build 核心代码
-
修改 watch 命令
json"watch": "run-p watch:*", "watch:ui": "npm run build:ui -- --watch", "watch:plugin": "npm run build:plugin -- --watch", "build:ui": "vite build --config vite.ui.config.ts", "build:plugin": "vite build --config vite.plugin.config.ts"Ï
然后这个模板就完成了🎉
我给放到了 github 上,懒得自己折腾的小伙伴可以直接 clone 到本地直接跑 github.com/alpacachen/...
(readme 全是 ai 写的)
bash
figma-react-template/
├── dist/ # 构建输出
├── src/
│ ├── plugin/ # 插件代码(在 Figma 中运行)
│ │ └── index.ts # 主插件代码
│ └── ui/ # UI 代码(在 WebView 中运行)
│ ├── app/
│ │ ├── app.tsx # 主 React 组件
│ │ ├── app.css # 样式
│ │ └── main.tsx # React 入口点
│ ├── index.html # HTML 模板
│ └── vite-env.d.ts # Vite 环境类型
├── .gitignore
├── eslint.config.js # ESLint 配置
├── manifest.json # Figma 插件清单
├── package.json # 项目依赖和脚本
├── tsconfig.json # TypeScript 配置
├── tsconfig.app.json # 应用的 TypeScript 配置
├── tsconfig.node.json # Node 的 TypeScript 配置
├── vite.plugin.config.ts # 插件的 Vite 配置
└── vite.ui.config.ts # UI 的 Vite 配置
现在前期准备弄完了,接下来真正开始
首先监听选中事件,可以让 ui.html知道选中了哪个图层,这样后面才好获取信息
ts
// plugin.ts
figma.showUI(__html__);
figma.on('selectionchange', () => {
const selection = figma.currentPage.selection;
if (selection.length == 1) {
const selectedNode = selection[0];
// 将选中图层的坐标大小和类型全部发送给 ui
const { x, y, width, height, type } = selectedNode;
figma.ui.postMessage({
x,
y,
width,
height,
type
});
}
});
ts
// 在 app.ts中监听 message,然后打印出来
useEffect(() => {
window.addEventListener('message', (event) => {
console.log(event.data.pluginMessage);
});
}, []);
然后测试一下
这样代码生成第一大步就完成了,因为代码生成说白了就是规则转化,把 figma 的字段转成 css 的属性。
本文到此结束,后面的章节会重点在 ui 界面的制作上,欢迎关注