Tailwind Export - 导出Tailwind的Figma插件
我只花了3天时间,便借助GPT4开发完了一个Figma插件,而我之前完全没有了解过任何Figma插件开发知识。
2023年3月27日
本文共有2279字,预计阅读时间11分钟
Introducing GPT-4
我想写一个Figma plugin,功能是将选定的元素生成的CSS样式,转换成tailwind类名并显示在插件面板上。插件面板默认没有元素,只有一句提示语“请选择元素”,在选择元素后,其样式对应的tailwind类名将会显示在插件面板上。下方会有一个复制按钮。如果再点击其他元素 ,继续生成新的tailwind类名。这个插件暂时只需要考虑宽、高和背景颜色,其他样式可以不用管。你需要告诉我code.ts里要怎么实现这个功能,给出完整代码,不能有typescript类型错误。
AI的回答
1// code.ts
2const figmaUIApi = `
3 <html>
4 <style>
5 body {
6 font-family: -apple-system, sans-serif;
7 margin: 0;
8 padding: 24px;
9 }
10
11 #info {
12 margin-bottom: 16px;
13 }
14
15 button {
16 background-color: #1669ef;
17 border: none;
18 border-radius: 4px;
19 color: white;
20 cursor: pointer;
21 font-size: 14px;
22 padding: 8px 16px;
23 }
24
25 button:hover {
26 background-color: #0d4edb;
27 }
28 </style>
29 <body>
30 <div id="info">请选择元素</div>
31 <button id="copy">复制</button>
32 <script>
33 const info = document.querySelector('#info');
34 const copyButton = document.querySelector('#copy');
35
36 copyButton.addEventListener('click', () => {
37 const tailwindClasses = info.textContent;
38 if (tailwindClasses) {
39 navigator.clipboard.writeText(tailwindClasses);
40 }
41 });
42
43 onmessage = (event) => {
44 const tailwindClasses = event.data.pluginMessage;
45 if (tailwindClasses) {
46 info.textContent = tailwindClasses;
47 } else {
48 info.textContent = '请选择元素';
49 }
50 };
51 </script>
52 </body>
53 </html>
54`;
55
56figma.showUI(figmaUIApi, { width: 300, height: 150 });
57
58function convertToTailwind(width: number, height: number, color: string): string {
59 // 宽度和高度转换
60 const widthClass = `w-${Math.round(width / 4)}`;
61 const heightClass = `h-${Math.round(height / 4)}`;
62
63 // 颜色转换
64 const colorClass = `bg-${color.replace('#', '')}`;
65
66 return `${widthClass} ${heightClass} ${colorClass}`;
67}
68
69figma.on("selectionchange", () => {
70 const node = figma.currentPage.selection[0];
71
72 if (node && node.type === "RECTANGLE") {
73 const width = node.width;
74 const height = node.height;
75 const color = node.fills[0].color;
76 const hexColor = `#${Math.round(color.r * 255).toString(16)}${Math.round(color.g * 255).toString(16)}${Math.round(color.b * 255).toString(16)}`;
77
78 const tailwindClasses = convertToTailwind(width, height, hexColor);
79 figma.ui.postMessage(tailwindClasses);
80 } else {
81 figma.ui.postMessage(null);
82 }
83});
Continue
界面
const figmaUIApi
跟着的那一大坨就是插件的界面,<style>
内的暂时不用管。copyButton.addEventListener
和onmessage
。copyButton.addEventListener
是给复制按钮添加了一个监听函数,将info
里的内容保存到tailwindClasses
里,如果存在则向剪贴板写入;onmessage
的作用是判断是否有选择元素,如果没有选择,会显示一个提示文案。插件逻辑
figma.showUI
是Figma的一个方法,用于召唤出插件的界面。function convertToTailwind
是AI为我们生成的一个最简单的转换函数,比如下面这个:1const widthClass = `w-${Math.round(width / 4)}`;
w-6
。figma.on
的作用是为指定事件注册一个回调函数,当这个事件发生的时候,执行该函数。1figma.on("selectionchange", () => {
2 const node = figma.currentPage.selection[0];
3
4 if (node && node.type === "RECTANGLE") {
5 const width = node.width;
6 const height = node.height;
7 const color = node.fills[0].color;
8 const hexColor = `#${Math.round(color.r * 255).toString(16)}${Math.round(color.g * 255).toString(16)}${Math.round(color.b * 255).toString(16)}`;
9
10 const tailwindClasses = convertToTailwind(width, height, hexColor);
11 figma.ui.postMessage(tailwindClasses);
12 } else {
13 figma.ui.postMessage(null);
14 }
15});
convertToTailwind()
处理函数,最终返回一串Tailwind类名字符串,然后将其显示到界面上。增加功能
1export default function Text(node: SceneNode): string {
2 const textNode = node as TextNode;
3
4 let fontSize = 'text-base';
5 let fontWeight = 'font-normal';
6 let fontColor = 'text-black';
7
8 const textAlign = ConvertTextAlign(textNode);
9
10 if (textNode.fontSize !== figma.mixed) {
11 fontSize = ConvertFontSize(textNode.fontSize);
12 }
13
14 if (textNode.fontWeight !== figma.mixed) {
15 fontWeight = ConvertFontWeight(textNode.fontWeight);
16 }
17
18 const opacity = ConvertOpacity(textNode.opacity);
19
20 if (textNode.fills !== figma.mixed) {
21 if (textNode.fills[0].type === "SOLID") {
22 fontColor = `text-${ConvertColors(textNode.fills[0].color)}`
23 }
24 }
25
26 return `${textAlign} ${fontSize} ${fontWeight} ${opacity} ${fontColor}`;
27}
1import getNumber from "./getNumber";
2
3const Sizes = {
4 'text-xs': 12,
5 'text-sm': 14,
6 'text-base': 16,
7 'text-lg': 18,
8 'text-xl': 20,
9 'text-2xl': 24,
10 'text-3xl': 30,
11 'text-4xl': 36,
12 'text-5xl': 48,
13 'text-6xl': 60,
14 'text-7xl': 72,
15 'text-8xl': 96,
16 'text-9xl': 128,
17}
18
19export default function ConvertFontSize(fontSize: number): string {
20 const fontSizeClass = getNumber(fontSize, Sizes);
21
22 return fontSizeClass;
23}
getNumber()
的作用就是在第二个参数中寻找与第一个参数最接近的名称。比如传入的字号是17,就会返回字符串text-base
。完工
align-items
、justify-content
、gap
都可以找到对应。所以我也特地生成了布局相关的类名。