Figma插件
2024 年 4 月 5 日

在2023年初,我发布了一个名为Placemark的Figma插件,该插件可以让你在Figma这款图形设计工具中创建矢量地图。从那时起,我一直在为这个插件进行维护,还推出了另一个名为Placemark Globe的插件。

它们在一定程度上取得了成功!根据Figma社区网站的数据,Placemark插件有11.3k用户,而Placemark Globe有1.5k用户。插件的反馈循环还可以-你可以看到插件收到了多少赞,以及有多少“用户”,但是没有随时间变化的统计数据,因此我使用Observable构建了自己的插件仪表板。

点击查看Observable上的Figma插件统计数据 (opens new window)。 即使如此,我并不确定是否有很多人实际在使用它们。也许“用户”指的是独立安装次数 - 它确实不表示活跃用户,因为我生成的图表只会“上升”,我知道活跃用户数量比那更不稳定。

沙盒 #

我对 Figma 插件很感兴趣,因为沙盒的挑战。插件本质上是第三方代码,可以访问用户的文档、信息,以及在一定程度上访问他们的电脑。你希望插件功能强大,同时又安全。

Figma拥有顶尖的工程团队,他们曾经努力使其运行。甚至首先基于领域实现的解决方案很快被发现存在安全隐患。

沙盒是一个非常有趣的话题,因为每个人都希望拥有它,但实现起来却不容易。 在实际应用中,很少有例子能够真正地发挥作用。例如,当我在为OpenStreetMap的地图编辑器iD (opens new window)工作时,我们总是面临着创建一个插件架构的挑战 (opens new window)来与JOSM (opens new window)竞争的问题,JOSM是一个更成熟的基于Java的桌面应用程序。

在Observable中的沙箱化 #

Observable (opens new window)是实际实现沙箱化的一个很好的例子。在我到达之前,Mike (opens new window)已经基本解决了整个挑战:架构如下:

  1. 用户代码在带有sandbox属性的iframe中运行。顶部框架通过postMessage (opens new window)与iframe进行通信。
  2. iframe可以向顶部框架进行回传,并且用户代码可能会恶意发送自己的消息(没有“受信任的通信)。 每个用户都有一个通配符子域,iframe 内容托管在这里,以防止 cookie 冲突。

这使我们意外地走得很远 - Observable 沙箱从安全角度来看基本上可以正常工作,但它也存在一些其他问题。例如,如果有人在 Observable 笔记本中编写了一个无限循环,它会将 iframe 进程占用到 100% 的 CPU 利用率,他们必须启用“安全模式”版本的应用程序才能摆脱无限循环。 浏览器供应商已经对此进行了一些调整,无论 iframe 是否为独立进程,或者与顶层页面共享,或者也许来自某个特定来源的所有 iframe 都有自己的进程。 这是一个复杂的话题。

在 Val Town 中的沙盒化 #

我写了一篇关于 Val Town 的长篇文章。 最近我们进行了一次关于沙箱技术的旅程。简而言之,该产品最初是基于Node.js的vm模块和其周围的包装器构建的,但很快意识到这种方法没有未来:所有在Node.js进程内尝试沙箱化的尝试都失败了。

因此,我们转向了由Node.js的创作者Ryan Dahl发起的Deno项目。Deno从一开始就拥有一个权限模型,让您可以启用或禁用程序对资源的访问。因此,最终您可以像安装模块并在其尝试读取您的/etc/passwd文件之前抓取它一样,并且实际上阻止它这样做。

Val Town使用node-deno-vm启动一个单独的Deno进程,它暴露 Deno 是一个非常酷的工具,到目前为止运行得非常好。

Figma中的沙盒 #

Figma 对沙盒问题的最终解决方案是使用 QuickJS (opens new window),这是一个小型的 JavaScript 引擎,可以编译为 WASM。实际上,你可以在 JavaScript 中使用 QuickJS 运行一个 JavaScript 引擎。

基本上,插件架构 (opens new window) 在一个 iframe 中运行插件的一部分,用于显示用户界面,另一部分在 QuickJS 中运行,使得代码可以实际读取和写入你的 Figma 文档。

现在,我已经编写了两个 Figma 插件,可以说这个方法是有效的。我遇到的最大问题是调试和性能。

JavaScript 的调试故事是非常重要的。 当QuickJS在Figma中遇到问题时,你会得到非常难以理解的错误。我花了很多时间猜测我的插件出了什么问题,因为我只能依靠类似上面的堆栈跟踪这样的东西。

很难量化,但Figma插件的性能也不是很好。他们最近推出了动态加载,应该会在一定程度上改善性能,但我的插件仍然运行速度比我感觉应有的慢。当然,我在开发地理空间插件,很容易忘记地理空间数据对系统有其独特的需求。地理空间工具会大量处理数据。 关于插件架构

我应该再次强调一下,Figma拥有一个插件架构,让许多人可以编写插件,这是多么了不起的事情。在网站上存在这种极为罕见。如果我试图列举另一个具有某种自由形式社区插件并在前端运行的网站,我几乎找不到。

我真的希望构建这种东西更容易!尽管人们经常谈论网络缺乏支付机制,但我们应该更加关注的是网络缺乏可扩展性机制:我们基本上只有iframes作为唯一的安全原语来启用插件架构,而它们还有很多不足之处。WebAssembly是未来(多年来一直是未来),但WebAssembly代码的分发和调试方式仍然未完全明确。

当然,沙箱化本身是一个难题。Lua脚本化视频游戏显然是其中的一个成功案例。网络可能会变得更好。 在我谈论沙盒化的问题之后,接下来我们再看看 Figma 插件开发流程的其他部分。

整体来说,情况还是不错的!发布新版本的 Figma 插件非常简便,只需点击几下,就能立即在社区网站上看到。成功可以在 Figma 社区网站 (opens new window) 上看到,那里有大量内容。还有许多其他 针对地图的插件 (opens new window),质量各异。

地标插件 #

注:这篇文章涉及许多不同的话题,你可能已经注意到了,这些话题在某种程度上是相关的。我本可以将它们编辑成更有连贯性的内容,但我只是为了写得开心,也许阅读这些对你来说也是一种乐趣。 Placemark Figma Plugin (opens new window) 是一个更为先进的项目。它直接从Overpass Turbo (opens new window)获取地图数据,这是一个用于OpenStreetMap (opens new window)数据的 API。相对于其他选项,这是相当有意义的:

  • 从栅格切片源加载地图会产生静态的栅格地图,无法进一步进行样式设置,而且还会引入另一层知识产权:像Mapbox或Maptiler这样的服务提供商的样式通常受版权保护。
  • 从矢量切片源加载瓦片在长期考虑可能是个好主意,但矢量瓦片通常针对性能和轻便性进行了优化,而不是完整性。目前使用我的插件,你确实可以得到整个 OpenStreetMap 的数据模型,甚至可以向地图添加树木。 OpenStreetMap插件赋予了一些额外的功能和简洁性,并且很明显,唯一需要的归属是OpenStreetMap标准归属 (opens new window),插件默认添加了此归属(安静,喷子!)

这是开源的,所以你可以看到它是如何实现所有这些功能的 (opens new window)。基本上,它实现了一个小型的静态地图渲染引擎,具有标签放置、样式、对OpenStreetMap标记方案的一些理解等功能。它使用d3的 (opens new window)Mercator地图投影 (opens new window)的实现,这在局部尺度上并没有太大区别。

该插件有两个主要的弱点:水体和缩放级别。

它在渲染水域方面表现得相当糟糕。这真的是一个很难解决的问题。 世界地图领域中的一个常见问题:假设您正在渲染一幅地图,显示一个沿海小镇,海洋在右侧。您怎么知道海洋在右侧?您是否下载了整个庞大的海洋多边形?那将极大地减慢您的渲染过程。有一些高效的方法可以做到这一点,这些方法相当令人惊讶且有趣:

  • 道路绘制的方向非常重要!它们必须绘制成陆地在道路的左侧,水在右侧(当查看道路箭头方向时)。如果您将此视为围绕陆地区域(例如岛屿)的描绘,则海岸线道路应按逆时针方向运行。- 来自OpenStreetMap维基 (opens new window)

然而,我还没找到时间来智能实现海岸线。这是开源的,所以如果您感到有灵感,我准备合并PR。

另一个问题只是缩放级别:为了呈现一个缩小的 地图,我需要制作不同的Overpass Turbo查询,以便下载的数据量更加合理,但我还没有这样做。

Placemark Globe插件 #

Placemark Globe (opens new window)插件更简单:它只是渲染一个地球。它甚至还没有渲染标签。

我认为创建这个插件的动机是,通常是“这还不存在”,但也是因为技术上简单的东西仍然可以很有用。总有一天我会找到既简单、有用又有利可图的东西!

这个插件更多地使用了d3 (opens new window) - 实际上,如果没有d3的辛勤工作,这是不可能的。插件的组件是将SVG转换为Figma的文档模型和样式系统,这只是有点不同。 相对于SVG,使用矢量地球仪有一些优势。这使得一切都更加方便。当然,生成矢量地球仪并将其导入到Figma中还有许多其他方法-这只是一个简单的方法。

由于这显示了边界,它使用了Visionscarto World Atlas (opens new window)项目的数据,这是Natural Earth (opens new window)的修改版本,以更加全球范围内被接受的方式显示一些有争议的边界。关于地图制图中的国际边界有很多文章,我在这里不打算总结。

插件架构 #

最初我使用create-figma-plugin (opens new window)创建了Globe插件。我认为它具有一些良好的模式。还有一些很好的插件开发资源 (opens new window)在其他地方。

基本上,我认为对于Figma插件来说,React太过于复杂。如果只有一些UI元素,几乎任何框架都太复杂了。只需编写一些HTML即可。 JavaScript不要想太多。我希望有一个独立的CSS框架,这样我就可以立即获得Figma的美学和深色模式支持。

保持这些Figma插件相当有趣。它让我可以满足尝试新技术的愿望,并帮助我在我依然喜欢的地图世界中保持一点存在感。