深入浅出 Sketch 插件开发

2022-05-05 12:01:11 浏览数 (2)

本文结合工程化实践,系统介绍要实现一个生产级别的 Sketch 插件所需要的技术知识和解决方案,并以一个真实的业务场景为案例,从0到1来实现一个生产级别的插件开发。

从 Sketch 插件开发的机制,自动打包更新工程化、Sketch 中 Objective-C 的运行机制等方面,分享开发一个生产级插件所需的技术储备。

最终开发一个如下图所示的 MVP插件将文章中讲到的知识串联起来,学以致用,加深对 Sketch 插件开发的理解。

在开发前,为了使开发调试更顺畅,需要做一点配置工作。

一、开发环境配置

崩溃保护

在 Sketch 插件开发过程中,会经常遇到崩溃问题,需要设置下崩溃保护,提高插件开发效率。

代码语言:javascript复制
defaults write com.bohemiancoding.sketch3 disableAutomaticSafeMode true

插件缓存

通过配置启用或禁用缓存机制:

代码语言:javascript复制
defaults write com.bohemiancoding.sketch3 AlwaysReloadScript -bool YES

HTTPS

另外在插件开发的过程中,涉及到 HTTP 请求,都需要使用 HTTPS,这是系统强制要求,否则会引起 Sketch 崩溃。

二、Sketch 插件开发基本知识

目前 Sketch 插件开发大概有两种方式,原生和混合开发模式:

原生开发指直接采用 Objective - C 或 Swift,并搭配 macOS 的 UI 框架 AppKit 进行开发,但这里有一个风险,由于 sketch API 更新快,经常会出现功能失效或者是兼容问题,维护成本也非常大。

混合开发即指使用 JavaScript CocoaScript 的混合开发模式。虽然 Sketch 官方提供了 API,但是这个API目前还不算完善,很多功能无法实现,因此还需要搭配CocoaScript访问更丰富的内部API,来实现更复杂的功能。

对于前端工程师来说,这里更推荐使用混合模式的方式来开发插件,即 webview 配合 JavaScript CocoaScript 的方式,既能发挥我们自身的技术优势,插件升级管理也更灵活。

环境配置

Sketch 插件可以使用官方提供 Skpm 开发套件来搭建开发环境,它是基于 Webpack 作为打包工具的。

项目结构

按照上面步骤生成官方的示例插件后,会在我们指定的目录生成项目文件,并且自动安装插件到 Sketch,在插件菜单就会看到刚刚安装好的插件。

这里需要说明的是,官方的示例文件里,对于 webview 是直接编译集成在插件里面的,这样有一个好处是,可以提高 webview 的加载速度,但是对于 webview 更新却不是很方便,如果要更新 webview 的话,就得更新整个插件体验上不是很好。

Manifest

在 Sketch 插件中,Manifest 文件相当于插件的入口文件,这个文件记录了作者信息、描述、图标以及获取插件更新的途径等信息。

还可以定义插件提供的命令,比如显示插件的工具栏,执行某些图层的操作等。Sketch 会在插件被加载时初始化菜单。

下面这个例子就表示用户在菜单栏点击切换工具栏就会显示 TeaMaker 的工具栏。

在 Manifest 文件中,还指定了插件更新的地址文件即 appcast.xml,Sketch 在启动的时候,会读取这个文件,当 appcast.xml 文件中插件版本有更新的时候,Sketch 会提示用户插件有更新,直接在 Sketch 就可以更新插件,如下所示:

appcast.xml

appcast.xml 文件是用来推送插件更新的,采取 Sketch 的这个更新机制来迭代开发很方便,来具体看看这个文件的字段以及所代表的意义:

主要是 enclosure 和 sparkle:version 这两个字段,表示插件新版本的地址和版本号。当有插件版本更新时,我们只需要添加一个 item,并且输入新版本插件的地址和版本号就可以。这里有一个地方需要注意的是,插件的地址一定是要外网可以公开访问的,并且是 https 的,这样 Sketch 才能读取并更新,否则就更新不了。

Webview 开发

对于前端工程师来说,开发 Sketch 插件还有一个很方便的地方就是官方提供了 webview 的方式来和 Sketch 进行交互,即我们可以使用传统的 web 开发技术来和 Sketch 进行数据的交互(比如可以发送数据给 Sketch 或者是 Sketch 返回数据到 web 端),这样我们前端开发工程师可以很方便的使用熟悉的技术高效开发 Sketch 插件。

webview 开发需要用到官方的 sketch-module-web-view 这个包,使用它提供的方法来把 web 网页加载进来,就可以像正常的 web 开发一样来开发。

使用下面的方法,在 sketch 中创建一个 webview 面板:

代码语言:javascript复制
import BrowserWindow from "sketch-module-web-view";
const options = {
    identifier: WebviewIdentifier,
    width: 1200,
    height: 800,
    center: true,
    closable: true,
    title: 'https://xxx',
    url: 'https://xxx'
};
browserWindow = new BrowserWindow(options);

在 url 字段传入远程的网页地址就可以显示 web 页面,其它的参数去官方的文档详细查看。

开发好 webview 后,还要打通 webview 与 Sketch 插件的数据通讯功能。这个官方也提供了相关的方法,在文档有详细的说明。

这里就简单的来说下,比如在 webview 中,想执行定义好在 Sketch 中的方法,可以这样做:

代码语言:javascript复制
window.postMessage('fill-picture') // 向 sketch 发送消息

插件开发部署自动化

当需要更新一个插件的时候,我们需要手动打包插件为 zip 文件,并且上传到外网服务器;还需要手动更新 appcast 文件,并发送外网,相当繁琐。

既然 skpm 是基于 node 环境来开发的,完全可以把这个过程自动化,当需要更新插件的时候只需要运行一个命令就可以。下面我们就来开始自动化构建的改造。

插件打包,上传自动化

插件打包和上传这块。我们需要把插件打包为 zip 文件,并且上传到外网服务器,这里以上传到腾讯云 cos 为例子来讲解。

先引入相关 zip 打包的库和腾讯云 COS 的 SDK:

代码语言:javascript复制
// 自动打包插件文件
const zip = new JSZip();
const COS = require('cos-nodejs-sdk-v5');

首先是插件打包,这里可以借助于 JSZip 这个第三方的包来完成插件的打包,使用起来非常方便,并且压缩率还是不错的,代码也很简单。

先创建一个 JSZip 实例:

代码语言:javascript复制
var zip =newJSZip();

使用 .file(fileName,fileContent) 添加一个txt文件或者是使用 .folder(folderName) 添加一个文件夹 :

代码语言:javascript复制
zip.file("Hello.txt","Hello Worldn"); // 添加文件
var img = zip.folder("images"); // 添加文件夹

然后是生成一个 zip 文件:

代码语言:javascript复制
zip.generateAsync({type:"blob"}).then(function(content){
    saveAs(content,"example0.1.zip");
});

生成 zip 文件这里是一个异步操作,下面的文件上传也会基于这个操作来进行,具体到我们插件打包这里,文件名称需要注意下,最好带上插件的版本号。

生成 zip 文件后们接下来就是文件上传了,这里以腾讯云 COS 为例来演示自动化文件上传的构建。

关于腾讯 COS 的 SDK 使用可以去官方文档详细了解,这里就不再赘述。

使用腾讯云 COS 来定义一个上传方法:

代码语言:javascript复制
function upload(file) {
    const filePath = path.resolve(cwd, file);
    const name = path.parse(filePath).name;
    const ext = path.parse(filePath).ext;
    const filename = `${name}${ext}`;
    
    cos.putObject({
        Bucket: ' XXXXXX',
        Region: ' XXXXXX',
        Key: filename,
        StorageClass: 'STANDARD',
        Body: fs.createReadStream(filePath),
        onProgress: (progressData) => {},
    }, (err, data) => {
        if (err) {
          console.log(err);
          console.log(`[${filePath}] 上传失败`);
        } else {
          console.log(`https://${data.Location}`);
          const zipUrl = `https://${data.Location}`;
        }
    });
};

上传完之后,它会返回文件在服务器上的下载地址。

定义好上传方法后,我们就可以在上面压缩 zip 文件后,调用这个定义好的上传方法来上传文件到腾讯云 COS :

代码语言:javascript复制
zip.generateAsync({type:"blob"}).then(function(content){
    saveAs(content,"example.zip");
}).then(function(){
    upload(path.resolve(__dirname, `./XXX.zip`));
});
插件更新推送自动化

准备工作就绪后,接下来就是在 appcast 文件添加新的插件下载地址,以及修改版本号,完成插件更新推送。

对于修改 xml 文件来说,如果直接使用 JavaScript 来修改的话,还是比较繁琐,我们先来看下这个文件的结构:

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <item>
      <enclosure url="https://...plugin.zip" sparkle:version="0.9.1"/>
    </item>
  </channel>
</rss>

如果使用 JavaScript 来修改,需要读取文件,并且使用正则表达式来匹配相关的标签来修改,非常繁琐。

进一步观察,这种结构化的文档和我们熟知的 json 数据格式是不是很像。可以使用 xml2js 这个库,专门用来把 xml 文档转换为 json 格式的文档,这样修改 xml 文件就变得非常简单。

思路如下,先把 xml 文档转换为 json 格式,对应到 Sketch 中的 appcast 文档中的其实就是修改 enclosure 和 sprkle:version 这两个字段,修改完之后,再把 json 转换为 xml 文档,就完成了 xml 文档的更新。

代码语言:javascript复制
// 更新 appcast.xml
function updateXml (url) {
  fs.readFile(appcast, function(err, data) {
    xml2js.parseString(data, function (err, result) {
        result.rss.channel[0].item.push({
            enclosure: [
              {
                $: {
                  url:xxx
                  url,
                  'sparkle:version': xxx,
                },
              },
            ],
        });
        const builder = new xml2js.Builder();
        const xml = builder.buildObject(result);
        fs.writeFileSync(appcast, xml);
    });
});
};

修改完之后,生成新的 xml 文档,然后再重上传腾讯云 COS 的步骤,把 xml 文档上传到腾讯云,就自动完成了插件打包,更新推送的工作。

把上面的几个步骤整合到 npm scripts 中,直接运行一个命令就可以更新推送插件。

插件调试

Sketch 插件开发过程,调试是一个必不可少的工作,JavaScriptCore 也有提供类似调试 JavaScript 代码打 log 的方式,我们可以在代码中放入:

代码语言:javascript复制
console.log/console.error

进行输出日志查看。

也可以借助官方开发的 sketch-dev-tools 插件来查看输出的相关信息。

在 Sketch 插件开发过程中,调试工作是非常重要的一环,特别是涉及到对 Sketch 文件的相关操作以及要用 CocoaScript 来访问内部 Sketch API 和 macOS 框架底层 API 功能的时候,如果是在插件里打断点来调试,每次要查看相关的日志和结果,都需要运行插件,繁琐,开发效率低下。

这里推荐使用 sketch 提供运行脚本的功能配合 sketch-dev-tools 工具来进行相关的开发调试,可以实时查看代码运行的结果。

使用这个运行脚步的功能,只需要在里面直接写相关的功能,点击运行代码,就可以查看运行结果,非常方便高效。

Objective-C 使用方法

Sketch 官方封装了一套 JS API ,但提供的功能很有限,只能够对 Sketch 文档进行一些常规的操作:Sketch 中的 Document、Artboard、Group、Layer 等进行相关操作以及导入导出等,具体使用可以去文档地址查看,有详细的说明。

强烈建议使用上面提到的运行脚本的功能来进行调试,可以对 API 有一个大概的感知。

在实际开发的过程中,需要通过调用 Sketch 内部 API 以及 macOS Cocoa frameworks 的能力,来实现更丰富的功能。

CocoaScript 实现了 JavaScript 运行环境到 Objective-C 运行时的桥接功能,可通过桥接器编写 JavaScript 外部脚本访问内部 Sketch API 和 macOS 框架底层的 API 功能。

使用 JavaScript 调用 Objective-C 的基本语法如下:

  1. Objective-C的方括号语法“[ ]”转换为JavaScript中的点“ . ”语法。
  2. “:” 冒号被转换为下划线“ _”, 最后一个下划线是可选的。
  3. Objective - C 属性设置,Getter: object.name(),object.setName('Sketch'),可以使用 setXXX 方法调用。

下面来演示下 Objective-C 具体的使用方法。

比如,在开发 Tea Maker 的过程中,我需要在侧边栏中按钮下面添加一行文字,如下图所示:

我们先在文档中搜索 NSButton 这个类,有一个 title 的属性:

点开这个方法,里面有这样一个说明:

代码语言:javascript复制
vartitle: String { get set }

根据我们上面说到的 JS 调用 Objective-C 的方法,我们在 Sketch 调用的时候,可以像下面这样来做:

代码语言:javascript复制
const button =  NSButton.alloc().init()
button.setTitle('文字信息')

这样就可以实现在按钮里添加文字。基本的知识准备好了,下面正式进入插件实战。

三、实战生产级插件开发

我们将会开发一个如下图所示的插件:

环境配置

先使用上面在环境配置章节讲到的,来配置下基本的开发环境:

代码语言:javascript复制
skpm create sketch-fill --template=skpm/with-webview

基本环境配置好后,运行下面的命令来安装插件:

代码语言:javascript复制
npm run postinstall

插件安装好后,在 Sketch 插件就可以找到刚安装好的插件:

下面来开发 webview,官方的静态文件是放在 recourse 目录下 html 文件中。

webview 创建

这里使用官方提供的 sketch-module-web-view 来创建 webview 容器,在插件 my-command.js 文件中使用下面的代码来实现 webview 容器:

代码语言:javascript复制
import BrowserWindow from "sketch-module-web-view";
const options = {
    identifier: WebviewIdentifier,
    width: 1200,
    height: 800,
    center: true,
    closable: true,
    title: 'https://xxx',
    url: 'https://xxx'
};
browserWindow = new BrowserWindow(options);

创建完容器之后,就可以创建具体的页面来开发插件功能。

使用传统的 html 来实现 webview 功能,直接在生成的 recourses 目录里修改 html 文件。

代码语言:javascript复制
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>sketch-fill</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  sketch-fill
  <div>
    <button id="button">填充图片</button>
  </div>
  <!-- 这里需要注意的是,编译后的脚本文件需要加上 recourses 前缀 -->
  <script src="../resources_webview.js"></script>
</body>
</html>

然后可以写一些简单的 CSS 来美化下。

接着在 webview.js 写按钮的功能,就是点击的时候发送填充图片的方法给 sketch 来调用插件里定义好的 fill-picture 方法来填充图片:

代码语言:javascript复制
// 取消页面右键点击事件
document.addEventListener('contextmenu', (e) => {
  e.preventDefault()
})

// 发送消息给 sketch 
document.getElementById('button').addEventListener('click', () => {
  window.postMessage('fill-picture', 'Called from the webview')
})

首先来思考下,要在 Sketch 填充一张图片,一般的操作流程是:

  1. 新建一个形状图层
  2. 选择形状图层
  3. 把图片填充到形状图层中去

通过上门三个步骤我们才能把图片正确的插入到 Sketch 中去。

这里我们使用 HTTP 的方式通过图片的 URL 来下载图片实现图片填充功能。

功能开发

像这样关于数据操作的功能开发,很适合先使用 Sketch 自带的运行脚本的功能来开发核心的功能,核心功能跑通后,再来整合到插件中,来提高插件开发效率。

梳理下图片填充这个流程:

  1. 获取用户选择的图层
  2. 对图层进行判断,是否是形状图层
  3. 通过用户传的 URL 来下载图片,进行填充

先在 Sketch 中画一个矩形并且选中,然后在 Sketch 中打开运行脚本的功能窗口,从 Sketch JS API 找到获取当前选中的图层的方法,运行脚本,就可以获取当前所选图层:

输出当前图层的信息,可以看到图层的各个属性,比如我们要对图层的类型进行判断,可以使用图层中的 type 字段来实现:

图层获取和判断完成之后,接下来就是填充图片到图层中去,这里要用 HTTP 的方式来下载 URL 指定的图片来填充,在 Sketch 开发中,需要用到原生方法来对 URL 进行处理。

在 OC 的文档中找 URL 的处理方法:

在 OC 中使用 NSURLRequest 这个类方法来处理,先使用 NSURL.URLWithString 方法来获取URL,然后使用 NSURLRequest.requestWithURL 方法来创建请求。

请求创建之后,然后发送请求,并且获取请求相应的数据,需要用到 NSURLConnection 这个方法,它主要是做两件事:

  1. 负责发送请求,建立客户端和服务端的连接,发送请求给服务器
  2. 收集服务器返回的数据

步骤:

  1. 创建一个 NSURL 对象,设置请求路径
  2. 传入 NSURL 并创建一个 NSURLRequest 对象,设置请求头和请求体
  3. 使用 NSURLConnection 发送请求

需要用到 NSURLConnection 中的 sendSynchronousRequest:returningResponse:error: 方法来请求数据,和上面的 URL 获取方法,整合起来就可以获取图片的数据:

代码语言:javascript复制
// 转换 URL
const request = NSURLRequest.requestWithURL(NSURL.URLWithString(url));
// 下载 URL 指向的图片
const data = NSURLConnection.sendSynchronousRequest_returningResponse_error(request, null, null);

获取图片资源之后,使用 NSImage 来加载图片数据资源以及使用 Sketch 内部的方法 MSImageData 来对图片数据进行初始化,这样才能最终得到图片的数据:

代码语言:javascript复制
// response 是下载下来的图片
const image = NSImage.alloc().initWithData( data );
const imageData = MSImageData.alloc().initWithImage(image);

数据准备后,使用 Sketch 原生的方法来进行图片填充实现,较之 JS API 方便些。

代码语言:javascript复制
// 先使用 sketchObject 方法,把 sketch JS API 提供的 fill 方法转换为原生方法
const fill = layer.sketchObject.style().fills().firstObject();
// 设置填充的类型是图片
fill.setFillType(4);
// 用图片来填充
fill.setImage( imageData );
// 图片使用填满的方式来填充矩形
fill.setPatternFillType(1);

对上面的方法进行一个简单的说明,这里使用原生 OC 的方法来填充图片,而获取图层的方法又是使用的 JS API 来获取的。所以需要把图层也就是 layer 进行一个转换,使用 Sketch 提供的转换方法 sketchObject 来进行转换,转换之后使用 OC 的原生方法来对图片数据进行相关操作。

Sketch 对形状的填充有颜色、渐变、图片。填充图片使用原生的方法是 fill.setFillType(4),然后使用 setImage 方法来填充图片。

这里还有一点需要注意点是,在填充图片的时候,图片的尺寸和用户选择的图片不可能刚好一样,所以还需要设置图片的填充方式,也就是图片自适应形状图层大小来进行缩放,使用 setPatternFillType(1) 。

具体这些方法有哪些参数,以及参数的作用,可以去 OC 官方的文档查看。

整合一下上面的代码,把它粘贴到运行脚本的窗口代码区域,运行一下就可以看到图片填充效果:

代码语言:javascript复制
let sketch =require("sketch")
let UI = sketch.UI

let doc = sketch.getSelectedDocument()
let selectedLayersObject = doc.selectedLayers
let selectedLayersArray = selectedLayersObject.layers

const imageUrl = "https://i.picsum.photos/id/428/600/300.jpg?hmac=jWM5_19UIAUyKXm0fdUShV5JrVKKJeP5Pntx6QUbGas"

// 转换 URL
const request = NSURLRequest.requestWithURL(NSURL.URLWithString(imageUrl));
// 下载 URL 指向的图片
const data = NSURLConnection.sendSynchronousRequest_returningResponse_error(request, null, null);

// 图片数据初始化
const image = NSImage.alloc().initWithData( data );
const imageData = MSImageData.alloc().initWithImage(image);

selectedLayersArray.forEach(layer => {

  if(layer.type ==="ShapePath") {
    // 先使用 sketchObject 方法,把 sketch JS API 提供的 fill 方法转换为原生方法
    const fill = layer.sketchObject.style().fills().firstObject();
    // 设置填充的类型是图片
    fill.setFillType(4);
    // 用图片来填充
    fill.setImage( imageData );
    // 图片使用自适应的方式来进行缩放
    fill.setPatternFillType(1);
  }
})

上面我们使用 Sketch 本身提供的脚本运行功能就完成了插件核心功能开发,开发调试都非常方便高效。

从上面整个过程也可以看到,在开发的时候,可以先把功能点,拆分成一个一个小功能点,使用脚本运行功能来边调试边开发。最后,再把它们整合到一起完成整个插件功能的开发。

整合发布插件

核心功能开发完后,接下来就是整合到代码中,完成插件开发并发布。

首先在插件里要响应 webview 发来的事件:

代码语言:javascript复制
import sketch,{ UI } from 'sketch';

 browserWindow.webContents.on('fill-picture', function(s) {
   // 响应 webview 的事件,开发具体的功能
   // 接收 webview 传过来的参数
   url = s.data 
});

响应事件后,先要对用户的选择来做一个判断,判断用户有没有选择图层并且是形状图层,定义一个方法来判断,使用官方提供的 JS API 就可以实现:

代码语言:javascript复制
function currentSelection() {
      let doc = sketch.getSelectedDocument()
      let selectedLayersObject = doc.selectedLayers
      let selectedLayersArray = selectedLayersObject.layers
      return selectedLayersArray;
}

使用上面的方法就可以判断用户是否选择图层了:

代码语言:javascript复制
browserWindow.webContents.on('fill-picture', function(s) {
   // 响应 webview 的事件,开发具体的功能
   // 接收 webview 传过来的参数
   url = s.data 
   let selectedLayersArray = currentSelection()
     if ( selectedLayersArray.length== 0 )
      {
        UI.message('请选择一个形状图层来插入图片');
      } else {
                
      }
});

从 webview 获取 URL 之后,不能直接在 Sketch 中使用,需要把上面的获取 URL 的代码封装为一个函数:

代码语言:javascript复制
function getUrl(url) {
      // 转换 URL
      var request = NSURLRequest.requestWithURL(NSURL.URLWithString(url));
      // 下载 URL 指向的图片
      var data = NSURLConnection.sendSynchronousRequest_returningResponse_error(request, null, null);
      if (data){
          return data;
      } else {
          UI.message('网络超时,再试试看');
          return;
      }
}

图片下载好了,接下来是填充图片到形状图层中去,也就是把上面用到的图片填充的代码封装为一个函数,定义一个填充图片的方法fillImage:

代码语言:javascript复制
function fillUrlImage(layer,url) {
    // 先判断图层是不是形状图层
    if(layer.type == 'ShapePath') {
          const fill = layer.sketchObject.style().fills().firstObject();
          let imageURL;
          // 下载图片
          imageURL = url;
          let response = getUrl(imageURL);
          let image = NSImage.alloc().initWithData( response );
          let imageData;
          imageData = MSImageData.alloc().initWithImage(image);
          fill.setFillType(4);
          fill.setImage( imageData );
          fill.setPatternFillType(1);
          UI.message('图片填充成功')
    } else {
        UI.message('请选择一个形状图层来插入图片');
        return;
    }
}

图片下载和图片填充的方法定义好之后,就可以把上面实现的功能代码整合到 my-command.js 文件中去:

代码语言:javascript复制
import BrowserWindow from 'sketch-module-web-view'
import { getWebview } from 'sketch-module-web-view/remote'
import UI from 'sketch/ui'
import sketch from 'sketch'

const webviewIdentifier = 'sketch-fill.webview'

const imageUrl = "https://i.picsum.photos/id/428/600/300.jpg?hmac=jWM5_19UIAUyKXm0fdUShV5JrVKKJeP5Pntx6QUbGas"

function currentSelection() {
  let doc = sketch.getSelectedDocument()
  let selectedLayersObject = doc.selectedLayers
  let selectedLayersArray = selectedLayersObject.layers
  return selectedLayersArray;
}

function getUrl(url) {
  // 转换 URL
  var request = NSURLRequest.requestWithURL(NSURL.URLWithString(url));
  // 下载 URL 指向的图片
  var data = NSURLConnection.sendSynchronousRequest_returningResponse_error(request, null, null);
  if (data){
      return data;
  } else {
      UI.message('网络超时,再试试看');
      return;
  }
}

function fillUrlImage(layer,url) {
  // 先判断图层是不是形状图层
  if(layer.type == 'ShapePath') {
        const fill = layer.sketchObject.style().fills().firstObject();
        let imageURL;
        // 下载图片
        imageURL = url;
        let response = getUrl(imageURL);
        let image = NSImage.alloc().initWithData( response );
        let imageData;
        imageData = MSImageData.alloc().initWithImage(image);
        fill.setFillType(4);
        fill.setImage( imageData );
        fill.setPatternFillType(1);
        UI.message('图片填充成功')
  } else {
      UI.message('请选择一个形状图层来插入图片');
      return;
  }
}

export default function () {
  const options = {
    identifier: webviewIdentifier,
    width: 240,
    height: 180,
    show: false
  }

  const browserWindow = new BrowserWindow(options)

  const webContents = browserWindow.webContents

  // 页面加载完,打印消息
  webContents.on('did-finish-load', () => {
    UI.message('UI loaded!')
  })

  // add a handler for a call from web content's javascript
  webContents.on('fill-picture', s => {
    UI.message(s)
    let selectedLayersArray = currentSelection()
    if ( selectedLayersArray.length== 0 )
    {
      UI.message('请选择一个形状图层来插入图片');
    } else {
      selectedLayersArray.forEach(layer => {
        fillUrlImage(layer,imageUrl)
      })
    }
  })

  browserWindow.loadURL(require('../resources/webview.html'))
}

// When the plugin is shutdown by Sketch (for example when the user disable the plugin)
// we need to close the webview if it's open
export function onShutdown() {
  const existingWebview = getWebview(webviewIdentifier)
  if (existingWebview) {
    existingWebview.close()
  }
}

整理完之后,运行 npm run build 命令来打包插件,然后安装,一个图片填充插件就开发好了。

如果还要添加其它的功能,就依照上面所说的方法,像搭积木一样,开发一个满足自己需求的插件。

总结

通过图片填充插件,来总结一下开发的流程:

  1. 首先使用官方提供的 Skpm 来初始化开发环境
  2. 使用官方提供 skpm-module-webview 来开发网页容器面板
  3. 使用你熟悉的技术来开发具体功能,比如, React 或者是 Vue。用 postMessage 的方式发送消息到插件端
  4. 在 sketch 端使用 JS API 或者是 OC 原生方法来实现相关的功能

在实际开发过程中,需要通过不断的查文档学习,不断的调试,这是一个必须要经历的过程,走完之后,你会发现,沉舟侧畔千帆过,病树前头万木春,更重要的是为业务赋能,提升团队工作效率。

0 人点赞