一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情。
书接前文 《Figma插件开发(二)》。本文介绍了插件登录鉴权、设计稿内容导出等相关内容。
插件内登录鉴权
Web中的身份认证
我们知道,HTTP 协议是无状态协议,服务端如何知道请求者的用户身份呢?这个过程我们叫做身份认证(Authentication)。Web系统中常用的身份认证可以分成两类:
-
基于 Session 的认证
-
基于 Token 的认证
我们常说的认证方式有:Cookie + Session、Cookie + Token、JWT等等,其实都可以归结为上述两种。
基于 Session 的认证,服务端会维护每个用户的 Session(会话)信息,客户端需要保存一个session-id,接口请求的时候带给服务端。通常会把session-id 保存在 cookie 里面,因为浏览器会自动把 cookie 放到 HTTP 请求的 header 中。这就是最经典、最常用的 Cookie + Session认证。
基于Token的认证,服务端不需要维护会话信息,而是根据用户信息生成一个Token 下发给客户端。客户端每次请求必须带上这个 Token,服务端收到Token 后会校验Token的合法性并解析出用户信息。客户端的Token可以保存在Cookie中,请求时会自动带上(这就是Cookie + Token)。也可以保存在 LocalStorage 中,请求时取出Token 并放到HTTP Header 中。通常 JWT 认证会选用后者。
可见无论选择哪种认证方式,都需要客户端存储一个认证信息,并请求时带给服务端。
Figma插件中的身份认证
前文已经讲过,Figma插件运行在一个Data URLs的 iframe中,无法使用 Cookie 和 LocalStorage。Figma提供了APIfigma.clientStorage
来替代LocalStorage。由于这些限制,JWT是比较合适的选择。
JWT全称JSON Web Token,也就是通过JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为
JSON对象
传输。在数据传输过程中还可以完成数据加密
、签名
等相关处理。
关于 JWT 认证的细节就不展开多说了,读者可自行了解。实际上其他认证方式也可以,受限的只是存储认证因子的方式和携带认证因子的方式。客户端需要做的就是登录的时候获取一个 Token 并保存到本地,每次请求的时候放到 Header中。
以 JWT 为例,Figma插件的身份认证流程如下:
OAuth 认证
前面的JWT 例子用的是用户名&密码的认证方式,实际中我们常用的是OAuth 认证,比如QQ、微信登录。
在浏览器中使用OAuth认证,通常是跳转到第三方页面,认证完成后再跳转回来,并携带上认证Token。但是在Figma插件中无法进行URL跳转,无法访问第三方页面,导致认证流程变得更加复杂。
Figma中发起登录请求后,打开外部浏览器进行认证,同时Figma轮询请求服务端获得认证结果,待浏览器中认证成功,服务就可以将认证结果返回给Figma。流程如下:
也可以参考官方文档中的 《OAuth with Plugins》。
设计稿导出
我们来实现一个功能:将选中的设计稿节点导出为png格式的图片。
整体流程为:
-
监听选中事件,获取当前节点id;
-
根据节点id,导出设计稿的buffer数据;
-
将buffer转换为图片格式,可以保存到本地或者上传到云端;
监听选中事件
选中设计稿会触发主线程的 selectionchange
事件,我们要在UI线程监听这个事件的话,需要传递一个回调。我们用前面讲到的rpct
来封装接口。
主线程注册方法:
// worker/plugin-methods.ts
type SelectionChangeCallback = (nodeIds: string[]) => void;
export class PluginMethods {
listenSelectionChange(onSelectionChange: SelectionChangeCallback) {
figma.on('selectionchange', () => {
const selection = figma.currentPage.selection;
return selection.map(x => x.id);
});
}
// ...
}
在插件中可以这样使用(vue组件代码):
const currentSelectId = ref<string | null>(null);
// 通过rpct直接调用主线程方法
pluginApi.listenSelectionChange(bindCallback(async (nodeIds) => {
const nodeId = nodeIds?.[0];
if (!nodeId) return;
currentSelectId.value = nodeId;
}));
导出buffer数据
figma节点有通用的方法,可以导出 UintuArray
格式的数据。同样可以定义在 PluginMethods
中,UI线程可以直接调用。
// worker/plugin-methods.ts
export class PluginMethods {
// ...
async getNodeImageBuffer(nodeId: string): Promise<Uint8Array> {
const node = figma.getNodeById(nodeId) as SceneNode;
const buffer = await node.exportAsync();
return buffer;
}
}
转换为图片
如果要在插件中展示所选的设计稿,那么可以将buffer
转换成base64
格式,然后使用 img
标签展示。
export async function arraybufferToBase64(data: ArrayBuffer) {
// Use a FileReader to generate a base64 data URI
const base64url = await new Promise<string>((r) => {
const reader = new FileReader();
reader.onload = () => r(reader.result as unknown as string);
reader.readAsDataURL(new Blob([data]));
});
return base64url;
}
<script lang=ts sfc>
// ...
const currentSelectId = ref<string | null>(null);
const currentSelectBase64 = ref<string>('');
pluginApi.listenSelectionChange(bindCallback(async (nodeIds) => {
const nodeId = nodeIds?.[0];
if (!nodeId) return;
currentSelectId.value = nodeId;
const buffer = await pluginApi.getNodeImageBuffer(currentSelectId.value);
currentSelectBase64.value = await arraybufferToBase64(buffer);
}));
</script>
<template>
<!-- 展示当前选择 -->
<img :src="currentSelectBase64">
<!-- ... -->
</template>
上传到云端
假设我们已经有一个后端写好的上传接口了。
// 上传设计稿
export async function uploadImage(fileName: string, buffer: Uint8Array): Promise<string> {
const formData = new FormData();
formData.append('file', new Blob([buffer.buffer], { type: 'image/png' }), fileName);
const rsp = await http.request({
method: 'POST',
url: '/figma/image/upload',
data: formData,
});
return rsp.data?.data.url;
}
保存到本地
有了图片的ArrayBuffer,我们可以浏览器的下载API保存图片。
// 下载设计稿
export async function downloadImage(fileName: string, buffer: Uint8Array): Promise<string> {
const blob = new Blob([buffer.buffer], { type: 'image/png' });
const url = window.URL.createObjectURL(blob);
const a = ducument.createElement('a');
a.href = url;
a.download = fileName;
a.click();
}
结语
本文介绍了开发Figma 插件用到的一些常用功能,示例代码可以参考figma-plugin-vue3 ——这是一个用Vue3搭建的编写Figma插件的模板项目。如果你在开发Figma插件中有遇到问题,欢迎一起交流讨论。