#87 制作一款给位图增加描边的 Figma 插件
这款插件叫 Image Stroke
Image Stroke 插件的主要目的是,为 Figma 中的位图图层添加矢量描边。
就像 PhotoShop 等软件中常见的 Layer Style - Stroke
效果一样,但在 Figma 中一直尚不支持。
做这个插件,一方面是为了自己的需求,另一方面是熟悉 Cursor 的开发流程。
如图所示,左图是一张透明背景的位图,直接对它添加 Stroke 的话会产生中图的效果,即对该图层而非图像内容添加描边。右图则是本插件预期达到的效果。
为什么使用 Image Stroke
传统添加描边方法有:
- 使用其他位图矢量化插件,调整合适参数后生成其内容整体的矢量图,再手动添加描边;
- 导出图片到 Adobe illustrator 进行扩展生成矢量化图形,再粘贴到 Figma 中;
- 导出图片到 Adobe photoShop 直接添加描边再导出,生成的依然是位图;
- …
Image Stroke 可以:
- 只专注于生成图像内容的轮廓描边,不处理图像内部细节。速度可能会更快一些(?)
- 一切都在 Figma 内部完成,不必打开 Adobe 系列等其他软件;
- 可以自动寻找透明度阈值(threshold),尽可能开箱即用;
- 永久免费;
当然 Image Stroke 也不是万能的:
- 轮廓识别算法依然有优化空间;
- 有时自动寻找透明度阈值(threshold)会失效,需要手动调节参数;
- 有些特殊的图片会失效,需要额外手动调整图片属性;
额外注意事项:
仅支持单个图像内容描边,一张图中有多个不相连接的图像时,会只生成最左边的;已修复尽量把图像放到 frame 外去使用插件,否则生成的描边位置会不对;已修复- 插件识别的是原图,所以无法生成裁剪后的图像路径。正确方式是裁剪后导出 png 再使用插件;
- Figma 会默认将一些大尺寸图片以0.5倍缩放(@2x)放进画布中,此时依然会按照原图识别路径缩放,直接缩放描边大小即可。
以上我会在下面详细说明。
过程与原理
首先我必须要声明,我不懂代码,插件大部分的代码都由 Cursor 完成。我的工作是提出需求,并在正确的方向上引导 Cursor 生成代码。
一开始我想的也是直接写一个矢量化功能,但是发现过于复杂,且效果不好。我开始寻找 PhotoShop 生成描边的算法,转而发现了 Distance Field 系列算法,Valve 曾改进此算法矢量化贴图并将应用到自家起源引擎中(Improved Alpha-Tested Magnification for Vector Textures and Special Effects)。然而这依然不是想要的,它应该很简洁才对。
经过一番搜索发现,Marching ants 算法可能是我想要的答案。这似乎是一种很古老又很常见的算法,只是我不身处这个领域尚未得知。经朋友点拨,找了些资料发给 Cursor,最终形成现在代码:
- 首先从图片的左上角开始,按列扫描像素,每列从上到下扫描;
- 找到第一个非透明像素点作为起始点;
- 以起始点为准,对其 8 方向像素透明度探索,直到找到下个轮廓点;
- 当检查的点本身不是透明像素且周围至少有一个有透明像素,即可认为是轮廓点;
- 按照一定方向和采样率追踪其他轮廓点,使路径单一且封闭;
- 在这个过程中使用 SVG 的 M 命令生成锚点,使用 L 连接下一个锚点。当首尾距离小于某个参数时,使用 Z 封闭路径;
经过一些黑盒测试后,设定了自动 threshold 所用到的参数与检测到第一个像素点透明度系数大约为2。不正确输出的话则 +1 直到 255。
值得一提的是,图片处理需要事先 decode 到 RGBA 数据。这在文档 Working with Images 有提到,但 Cursor 默认是不知道的。所以作为 AI 辅助代码的使用者,即使不亲自写代码,也还是需要知道代码逻辑关系的。而 Cursor 感觉也可以往这个方向迭代一下,比如在 notepads 之外选择自带的一些知名库的文档一键投喂。
参数说明
描边宽度和描边颜色
不多赘述,按照 Figma 中 Storke 属性数值填写就好。描边拐角已默认设置圆滑;
采样率
值越大,轮廓越简单(1-10),锚点越少,细节更少。如下图所示。
- 需要注意采样率和图片尺寸也有关系:
- 图片较大,则可以使用较大的采样率;图片较小时需要用较小的采样率;
- 总之建议从 1 开始尝试;
- 采样率不匹配时不会生成路径;
- 注意:圆形边缘属于复杂边缘;
简化容差
值越大,路径越平滑。如下图所示。
- 整体调节逻辑和采样率类似,也和边缘复杂度、图片大小有关系;
- 但不建议两者同时调大,会导致路径过于简陋……
透明度阈值
类似矢量化算法中的 threshold,与图片边缘清晰度有关系。如下图所示,我用 blur 手动降低了图片边缘清晰度,边缘越模糊越需要减小阈值来贴合边缘。
- 边缘越模糊或有半透明过渡,需要调整阈值来准确捕获边缘。特别是使用一些 remove background 工具后,图片边缘的透明度会变复杂,要想达到理想的效果,需要实际放大看下,并不断尝试阈值。
- 有时阈值可能会相当小;
闭合路径阈值
当收尾锚点距离小于这个值时闭合路径,否则继续追踪轮廓。在原理中会提到。一般不用改,所以作为高级设置隐藏起来了。
缺点说明
轮廓识别算法依然有优化空间
目前是从上到下从左到右线性地开始扫描,如果能从四周开始扫描可能效率会更高?我不确定,而且可能多个追踪方向会影响路径的闭合。
有时自动寻找透明度阈值(threshold)会失效,需要手动调节参数
就是这样。如果要手动设置的话,你可以放大图片直到看到方形像素。找到左上方第一个像素点,试着判断一下这个像素块的透明度阈值多少。像素透明度越低,需要设置的阈值参数就越低。
有些特殊的图片会失效,需要额外手动调整图片属性
目前仅支持 PNG 图片,你可以手动导出一下(或 ⇧⌘C)。因为测试图片数量有限,所以难免会出现怎么调节参数都不行的状况。有精力的话我会再优化一下。
插件识别的是原图,所以无法生成裁剪后的图像路径。正确方式是裁剪后导出 png 再使用插件;
同上,有时间的话再修复。
没有开源仓库
由于代码大部分是 AI 生成的,而且难度不高,我觉得上传 GitHub 多少有些不合适。如果有多个人好奇源码,我再考虑一下开 repo。
如果你觉得文章对你有些帮助,可以请我的猫吃罐头 ↓