Figma 一直鼓励开发人员和设计师之间的合作。 它致力于无穷无尽的社区制作插件宝库。 需要 3D 元素? 有一个插件。 需要抽象的 SVG? 有一个插件了。
也就是说,Figma 的设计部分一直是相对静态的——总是使用通过预定义的用户交互相互连接的不可移动的矩形。 但是,如果我告诉你,你的设计可以突然变得生动——它们可以是动画的、交互的,甚至是有状态的,你会怎么做? 那么,如何将概念与实现分开?
FIGMA 6月宣布 它正在将 JavaScript 驱动的小部件带到桌面上。 现在,设计人员可以直接在 Figma 中浏览和实现逻辑驱动的组件!
打招呼 小部件 API! 您想知道它是什么以及如何使用它吗? 这正是我们将在这篇文章中一起做的事情。
Figma 小部件开启了无数可能性
想象一下,您正在与您的合作伙伴日以继夜地设计一个大型餐厅应用程序。 你们已经在同一个 Figma 板上进行了合作; 你们俩都在共享完全相同的文档,并且会即时发生更改。
当然,您已经知道协作涉及的不仅仅是设计过程:
- 项目管理,
- 主持民意调查以收集选票,
- 导入和可视化模拟数据,
- 甚至可能在工作数小时后玩多人游戏来冷静下来。
我们只需要一个人来管理所有内容并将链接发送给该组的其他成员。 但是,哦,这不是很有效,是吗?
好吧,这就是小部件发挥作用的地方。 可以想象,我们可以在不离开 Figma 的情况下完成所有这些——是的,所有事情。
以下是您可能希望在 Figma 中使用小部件的几种方式:
清单去 不断. 如您所知,您可以在文档中自由使用大量小部件。 事实上,您可以从 Widgets 菜单(Shift
+I
).
但是我们不是来学习如何使用小部件的,因为这很容易。 让我们做我们最擅长的事情:我们将创建自己的 Figma 小部件! 这将受到启发 Chris Coyier 的设计报价网站. 我们将获取 API,将其输入到小部件中,然后直接在 Figma 中显示随机设计报价。
这是我们需要的
我不喜欢成为坏消息的承担者,但是为了开发小部件,你必须在 Windows 或 Mac 上。 Linux 用户,我很抱歉,但你不走运。 (你还可以 使用虚拟机 如果你想跟随。)
我们要 下载 Figma 桌面 应用。 最简单的入门方法是直接从应用程序生成一个小部件模板。
让我们通过打开小部件菜单(Shift
+ I
),切换到 研发支持 选项卡,并创建一个新项目。
之后,Figma 将提示您为新小部件命名并决定它是否更适合 设计板或 FigJam 板 也。 对于本文的目的,前一个选项就足够了。
定制并不止于此; Figma 还将为您提供从预制计数器小部件或支持 iFrame 的替代方案开始的选项,它还允许您访问 Canvas 和 Fetch API(以及所有其他浏览器 API)。 我们将使用简单的“Empty”选项,但最终我们会自己修改它以使用 Fetch API。
然后系统会提示您将新的小部件项目保存到系统中的一个特殊目录中。 完成后,启动终端并将其定向到该文件夹。 暂时不要运行任何命令——我们稍后会这样做,并且为了了解更多关于 Widgets API 的信息而故意出错。
设计小部件
我们直接从 Chris Coyier 的设计报价网站. 因此,让我们通过启动 DevTools 去那里深入研究。
我在这里使用的两个快捷键是 Ctrl
+Shift
+C
(或 Cmd
+Shift
+C
) 切换“选择元素”工具,以及 Shift
+Click
将颜色格式更改为 HEX 代码。 我们这样做是为了了解 Chris 网站中使用的颜色、字体、字体粗细和字体大小。 所有这些信息对于在 Figma 中构建一个非常相似的小部件至关重要,这将是我们的下一步! 你可以 抓取设计好的组件 并在您自己的画布中使用它。
我不会在这里详细介绍,因为本文的主要主题是通过编写代码来构建小部件。 但我怎么强调照顾好你的重要性都不为过 小部件的样式… CSS-Tricks 已经有很多 面向设计的 Figma 教程; 您不会后悔将它们添加到您的阅读列表中。
为我们的小部件创建布局
随着设计的结束,是时候放下我们的编程手指并开始构建我们的小部件的齿轮了。
Figma 如何将其设计构建块转换为类似 React 的组件是非常有趣的。 例如,具有自动布局功能的框架元素表示为 <AutoLayout />
代码中的组件。 除此之外,我们将使用另外两个组件: <Text />
和 <SVG />.
看看我的 Figma 板......我正是要求你专注于对象树。 我们需要能够将我们的小部件设计转换为 JSX 代码。
如您所见,我们的设计报价小部件需要导入三个组件。 考虑到 完整的API 仅包含八个基于层的节点。 但是您很快就会看到,这些模块足以制作各种布局。
// code.tsx
const { widget } = figma;
const { AutoLayout, Text, SVG } = widget;
有了这个,我们就拥有了像在 React 中一样构建小部件骨架所需的一切:
function QuotesWidget() {
const quote = `...`;
const author = `...`;
return (
<AutoLayout>
<SVG />
<AutoLayout>
<Text>{quote}</Text>
<Text>— {author}</Text>
</AutoLayout>
<SVG />
</AutoLayout>
);
}
widget.register(QuotesWidget);
至少可以说,这段代码非常令人困惑。 现在,我们无法区分设计层。 值得庆幸的是,我们能够通过使用 name
属性。
<AutoLayout name={"Quote"}>
<SVG name={"LeftQuotationMark"} />
<AutoLayout name={"QuoteContent"}>
<Text name={"QuoteText"}>{quote}</Text>
<Text name={"QuoteAuthor"}>— {author}</Text>
</AutoLayout>
<SVG name={"RightQuotationMark"} />
</AutoLayout>;
而且,当然,我们仍然看不到引号 SVG,所以让我们努力修复它。 这 <SVG/>
组件接受一个 src
获取 SVG 元素的源代码的属性。 这个没什么好说的,所以让我们保持简单,直接跳回代码:
const leftQuotationSvgSrc = `<svg width="117" height="103" viewBox="0 0 117 103" fill="none" xmlns="<http://www.w3.org/2000/svg>">
// shortened for brevity
</svg>`;
const rightQuotationSvgSrc = `<svg width="118" height="103" viewBox="0 0 118 103" fill="none" xmlns="<http://www.w3.org/2000/svg>">
// shortened for brevity
</svg>`;
function QuotesWidget() {
return (
<SVG name={"LeftQuotationMark"} src={leftQuotationSvgSrc} />
<SVG name={"RightQuotationMark"} src={rightQuotationSvgSrc} />
);
}
我想我们都同意现在一切都清楚多了! 当我们命名事物时,它们的目的突然对我们代码的读者变得更加明显。
实时预览我们的小部件
Figma 在构建小部件时提供了出色的开发人员体验,包括(但不限于) 热重载. 借助此功能,我们能够实时编码和预览小部件的更改。
通过打开小部件菜单开始 (Shift
+I
),切换到开发选项卡并单击或将新小部件拖动到板上。 无法找到您的小部件? 不用担心,只需单击三点菜单并导入您的小部件 manifest.json
文件。 是的,这就是让它恢复存在所需要的一切!
等等,您是否在屏幕底部收到错误消息?
如果是这样,让我们调查一下。 点击 ”打开控制台”并阅读它要说的内容。 如果 打开控制台 按钮不见了,有另一种方法可以打开调试控制台。 单击 Figma 徽标,跳转到小部件类别并显示开发菜单。
该错误可能是由于我们尚未将 TypeScript 编译为 JavaScript。 我们可以通过运行在命令行中做到这一点 npm install
和 npm run watch
。 (或者 yarn
和 yarn watch
)。 这次没有错误!
您可能遇到的另一个障碍是,任何时候更改代码时,小部件都无法重新渲染。 我们可以使用以下上下文菜单命令轻松地强制我们的小部件更新: 小工具 → 重新渲染小部件.
样式化小部件
按照目前的情况, 容貌 我们的小部件离我们的最终目标还很远。
那么我们如何从代码中设置 Figma 组件的样式呢? 也许像我们在 React 项目中那样使用 CSS? 消极的。 使用 Figma 小部件, 所有 样式通过一组 有据可查的道具. 幸运的是,这些物品几乎都被命名了 同样地 给他们在 Figma 中的同行。
我们将从配置我们的两个开始 <AutoLayout />
成分。 正如您在上面的信息图中所见,道具名称非常能描述它们的用途。 这使我们可以轻松地直接跳入代码并开始进行一些更改。 我不会再展示整个代码,所以请依靠组件名称来指导您片段所属的位置。
<AutoLayout
name={"Quote"}
direction={"horizontal"}
verticalAlignItems={"start"}
horizontalAlignItems={"center"}
spacing={54}
padding={{
horizontal: 61,
vertical: 47,
}}
>
<AutoLayout
name={"QuoteContent"}
direction={"vertical"}
verticalAlignItems={"end"}
horizontalAlignItems={"start"}
spacing={10}
padding={{
horizontal: 0,
vertical: 0,
}}
></AutoLayout>
</AutoLayout>;
我们刚刚取得了很大的进步! 让我们保存并跳回 Figma,看看我们的小部件是什么样子的。 还记得 Figma 如何在新更改时自动重新加载小部件吗?
但它还没有完全实现。 我们还必须为根组件添加背景颜色:
<AutoLayout name={"Quote"} fill={"#ffffff"}>
再一次,看看你的 Figma 板,注意变化是如何几乎立即反映到小部件中的。
让我们继续本指南并设置样式 <Text>
组件。
看一看之后 小部件 API 文档,再次清楚的是,属性名称几乎与 Figma 应用程序中的对应名称相同,如上图所示。 我们还将使用上一节检查 Chris 网站的值。
<Text name={'QuoteText'}
fontFamily={'Lora'}
fontSize={36}
width={700}
fill={'#545454'}
fontWeight={'normal'}
>{quote}</Text>
<Text name={'QuoteAuthor'}
fontFamily={'Raleway'}
fontSize={26}
width={700}
fill={'#16B6DF'}
fontWeight={'bold'}
textCase={'upper'}
>— {author}</Text>
向小部件添加状态
我们的小部件当前显示相同的报价,但我们希望随机从整个报价池中提取。 我们必须添加 州 到我们的小部件,所有 React 开发人员都知道它是一个变量,其更改会触发我们组件的重新渲染。
在 Figma 中,状态是用 useSyncedState
钩; 差不多 反应的 useState
,但它需要程序员指定一个唯一的键。 这个要求源于 Figma 必须同步我们的小部件的状态。 所有 可能正在查看相同设计板但通过不同计算机的客户。
const { useSyncedState } = widget;
function QuotesWidget() {
const [quote, setQuote] = useSyncedState("quote-text", "");
const [author, setAuthor] = useSyncedState("quote-author", "");
}
这就是我们现在需要的所有改变。 在下一节中,我们将弄清楚如何从 Internet 获取数据。 剧透警报:它并不像看起来那么简单。
从网络中获取数据
回想一下,当 Figma 让我们选择从支持 iFrame 的小部件开始时。 虽然我们没有选择那个选项,但我们仍然必须实现它的一些功能。 让我解释一下为什么我们不能简单地打电话 fetch()
在我们的小部件代码中。
当您使用小部件时,您是在自己的计算机上运行由其他人编写的 JavaScript 代码。 虽然所有小部件都经过了 Figma 员工的彻底审查,但它仍然是一个巨大的安全漏洞,因为我们都知道有多少 哪怕是一行 JavaScript 也能造成伤害.
因此,Figma 不能简单地 eval()
匿名程序员编写的任何小部件代码。 长话短说,团队认为最好的解决方案是在严密保护的沙盒环境中运行第三方代码。 正如您可能已经猜到的那样,浏览器 API 在这样的环境中不可用。
不过不用担心,Figma 对第二个问题的解决方案是 <iframe>
s。 我们在文件中编写的任何 HTML 代码,最好称为 ui.html
,将有权访问所有浏览器 API。 您可能想知道我们如何从小部件中触发此代码,但我们稍后会对此进行研究。 现在,让我们回到代码:
// manifest.json
{
"ui": "ui.html"
}
<!-- ui.html -->
<script>
window.onmessage = async (event) => {
if (event.data.pluginMessage.type === 'networkRequest') {
// TODO: fetch data from the server
window.parent.postMessage({
pluginMessage: {
// TODO: return fetched data
}
}, '*')
}
}
</script>
这是widget-to-的通用模板iframe
沟通。 让我们使用它从服务器获取数据:
<!-- ui.html -->
<script>
window.onmessage = async (event) => {
if (event.data.pluginMessage.type === 'networkRequest') {
// Get random number from 0 to 100
const randomPage = Math.round(Math.random() * 100)
// Get a random quote from the Design Quotes API
const res = await fetch(`https://quotesondesign.com/wp-json/wp/v2/posts/?orderby=rand&per_page=1&page=${randomPage}&_fields=title,yoast_head_json`)
const data = await res.json()
// Extract author name and quote content from response
const authorName = data[0].title.rendered
const quoteContent = data[0].yoast_head_json.og_description
window.parent.postMessage({
pluginMessage: {
authorName,
quoteContent
}
}, '*')
}
}
</script>
我们省略了错误处理以保持简单和中肯。 让我们回到小部件代码,看看我们如何访问定义在 <iframe>
:
function fetchData() {
return new Promise<void>(resolve => {
figma.showUI(__html__, {visible: false})
figma.ui.postMessage({type: 'networkRequest'})
figma.ui.onmessage = async ({authorName, quoteContent}) => {
setAuthor(authorName)
setQuote(quoteContent)
resolve()
}
})
}
如您所见,我们首先告诉 Figma 公开访问我们隐藏的 <iframe>
并使用名称触发事件 "networkRequest"
. 我们正在处理这个事件 ui.html
通过检查文件 event.data.pluginMessage.type === 'networkRequest',
然后将数据发布回小部件。
但是什么都没有发生……我们还没有打电话给 fetchData()
功能。 如果我们直接在组件函数中调用,控制台会出现如下错误:
Cannot use showUI during widget rendering.
Figma 告诉我们不要打电话 showUI
直接在函数体中……那么,我们应该把它放在哪里呢? 答案是一个新的钩子和一个新的功能: useEffect
和 waitForTask
. 您可能已经熟悉 useEffect
如果您是 React 开发人员,但我们将在此处使用它在挂载小部件组件时从服务器获取数据。
const { useEffect, waitForTask } = widget;
function QuotesWidget() {
useEffect(() => {
waitForTask(fetchData());
});
}
但这将导致另一个“错误”,我们的小部件将永远使用新报价重新渲染。 发生这种情况是因为 useEffect
,根据定义,当小部件的状态发生变化时再次触发,不,当我们调用时 fetchData
。 同时 有一种技术 只打电话 useEffect
一旦在 React 中,它就不适用于 Figma 的实现。 来自 Figma 的文档:
由于小部件的运行方式,
useEffect
应该处理以相同状态多次调用。
值得庆幸的是,有一个简单的解决方法,我们可以利用并调用 useEffect
只有在组件第一次挂载时才进行一次,它是通过检查状态的值是否仍然为空:
function QuotesWidget() {
useEffect(() => {
if (!author.length & !quote.length) {
waitForTask(fetchData());
}
});
}
你可能会遇到一个可怕的“内存访问越界” 错误。 它是 在插件和小部件开发中很常见. 只需重新启动 Figma,它就不会再存在了。
您可能已经注意到,有时,引用文本包含奇怪的字符。
这些是 Unicode字符 我们必须在代码中正确格式化它们:
<!-- ui.html -->
<script>
window.onmessage = async (event) => {
// ...
const quoteContent = decodeEntities(data[0].yoast_head_json.og_description);
};
// <https://stackoverflow.com/a/9609450>
var decodeEntities = (function () {
// this prevents any overhead from creating the object each time
var element = document.createElement("div");
function decodeHTMLEntities(str) {
if (str && typeof str === "string") {
// strip script/html tags
str = str.replace(/<script[^>]*>([Ss]*?)</script>/gim, "");
str = str.replace(/</?w(?:[^"'>]|"[^"]*"|'[^']*')*>/gim, "");
element.innerHTML = str;
str = element.textContent;
element.textContent = "";
}
return str;
}
return decodeHTMLEntities;
})();
</script>
和 瞧,我们的小部件每次添加到设计板时都会获取全新的设计报价。
向我们的小部件添加属性菜单
虽然我们的小部件在实例化时会获取新的报价,但如果我们可以再次执行此过程但不删除它会更实用。 本节将很短,因为解决方案非常出色。 和 属性菜单,我们可以通过调用 usePropertyMenu
钩。
const { usePropertyMenu } = widget;
function QuotesWidget() {
usePropertyMenu(
[
{
itemType: "action",
propertyName: "generate",
tooltip: "Generate",
icon: `<svg width="22" height="15" viewBox="0 0 22 15" fill="none" xmlns="<http://www.w3.org/2000/svg>">
<!-- Shortened for brevity -->
</svg>`,
},
],
() => fetchData()
);
}
通过一个简单的钩子,我们可以创建一个按钮,当它被选中时,它会出现在我们的小部件附近。 这是我们为了完成这个项目而需要添加的最后一块。
向公众发布我们的小部件
如果没有人,构建小部件没有多大用处 使用 它。 而同时 Figma 授予组织启动的选项 私立 小部件 对于内部使用,向世界发布这些小程序更为常见。
Figma 有一个精细的小部件审核流程,可能需要 5 到 10 个工作日。 虽然我们一起构建的设计引用小部件是 已经在小部件库中,我仍然会展示它是如何到达那里的。 请不要尝试再次重新发布此小部件,因为这只会导致删除. 但是,如果您对其进行了一些重大更改,请继续与社区分享您自己的小部件!
通过单击小部件菜单 (Shift
+I
) 并切换到 研发支持 选项卡以查看我们的小部件。 单击三点菜单并按 发布.
Figma 会提示您输入有关小部件的一些详细信息,例如标题、描述和一些标签。 我们还需要一个 128×128 的图标图像和一个 1920×960 的横幅图像。
导入所有这些资产后,我们仍然需要小部件的屏幕截图。 关闭发布模式(不用担心,您不会丢失数据)并右键单击小部件以显示一个有趣的上下文菜单。 找出 复制/粘贴为类别并选择 复制为 PNG.
完成后,让我们回到发布模式并粘贴小部件的屏幕截图:
向下滚动并最终发布您的模式。 庆祝! 🎉
Figma 将在几天后与您联系,了解您的模态评论状态。 在被拒绝的情况下,您将获得 有机会进行更改并再次提交.
结论
我们刚刚从头开始构建了一个 Figma 小部件! 这里有很多东西没有涉及到,比如 点击事件, 输入表格及 更多. 您可以在中深入了解小部件的完整源代码 这个GitHub仓库.
对于那些渴望将他们的 Figma 技能提升到更高水平的人,我建议探索 Widgets 社区并使用吸引您眼球的东西作为灵感。 继续构建更多的小部件,不断提高你的 React 技能,甚至在你意识到之前,你就会教我如何做这一切。
更多资源
在制作这个小部件时,我不得不参考大量文档。 我想我会分享我发现的最有帮助的东西。