Sketch 是近些年比较流行的 UI 设计软件,它比起以前经常使用的 Illustrator 或者 Photoshop 比较好的地方在于小巧功能简单但足够,同时对 Mac 的触摸板支持更加友好。另外它的插件系统也要比 Adobe 更加友好,大量的插件帮助咱们解决协同和效率上的问题。javascript
Sketch 插件最大的好处在于能够直接使用 JavaScript 进行开发,并提供了许多配套的开发工具。下面我就以帮助设计师同窗快速插入占位图的插件 Placeholder 为例,带你们一步一步的了解如何进行 Sketch 插件开发。html
在进行插件开发以前,咱们须要了解一些基础的知识。Sketch 是一套原生 Objective-C 开发的软件,它之因此能支持使用 JS 开发,是由于它使用 CocoaScript 做为插件的开发语言。它就像是一座桥(Bridge),能让咱们在插件中写 OC 和 JS,而后 Sketch 将基础方法进行了封装,实现了一套 JavaScript API,这样咱们就能使用 JS 开发 Sketch 插件了。前端
注: 关于如何开发插件,官方提供了一份入门教程 《Create a plugin》,在阅读下文以前,也能够花 2~3min 先看看这篇官方教程,内容比较简短。
在进行插件开发以前,咱们捋一捋咱们须要实现的功能。http://placeimg.com/ 是一个专门用来生成占位图的网站,咱们将利用该网站提供的服务制做一个生成指定大小的占位图并插入到 Sketch 画板中的功能。插件会提供一个面板,可让使用者输入尺寸、分类等可选项,同时提供插入按钮,点击后会在画板插入一张图片图层。java
skpm
是 Sketch 官方提供的插件管理工具,类比于 Node.js 中的 npm
。它集插件的建立、开发、构建、发布等多项功能于一体,咱们在不少场景都须要使用它。安装的话比较简单,直接使用 npm
全局安装便可。node
npm install -g skpm
按照官方教程,安装完毕以后咱们就可使用 skpm create
命令来初始化项目目录了。固然 skpm
是支持基于模板初始化的,官方仓库也列举了一些模板,咱们可使用 --temlate
来指定模板进行初始化。不过处于教学的目的,我这里就仍是使用官方默认的模板建立了。react
➜ ~ skpm create sketch-placeimg ✔ Done! To get started, cd into the new directory: cd sketch-placeimg To start a development live-reload build: npm run start To build the plugin: npm run build To publish the plugin: skpm publish
skpm
内部会使用 webpack
进行打包编译,运行 npm run build
会生成 sketch-placeimg.sketchplugin
目录,该目录就是最终的插件目录。双击该目录,或者将该目录拖拽到 Sketch 界面上就成功安装插件了。和 webpack --watch
相似,运行 npm run watch
的话对监听文件变化实时编译,在开发中很是有帮助。webpack
注: 不要使用npm start
进行开发,它携带的--run
命令会使得 构建速度特别慢。虽然它带 Live Reload 功能会很方便,但在官方未修复该问题前仍是不建议你们使用。
建立好的模板目录结构以下,为了帮助你们理解,咱们来简单的介绍下这些目录和文件。git
. ├── README.md ├── assets │ └── icon.png ├── sketch-assets │ └── icon.sketch ├── sketch-placeimg.sketchplugin │ └── Contents │ ├── Resources │ │ └── icon.png │ └── Sketch │ ├── manifest.json │ ├── my-command.js │ └── my-command.js.map ├── node_modules ├── package.json └── src ├── manifest.json └── my-command.js
和大多数 JS 项目同样,skpm
建立的项目中也会有 package.json
文件。该文件除了像以前同样记录了项目的依赖和快捷命令以外,还增长了 skpm
字段用来对 skpm
进行配置,默认的值以下。github
{ ... "skpm": { "name": "sketch-placeimg", "manifest": "src/manifest.json", "main": "sketch-placeimg.sketchplugin", "assets": [ "assets/**/*" ], "sketch-assets-file": "sketch-assets/icons.sketch" }, ... }
这里指定了该插件的名称为 sketch-placeimg
,插件的 manifest
文件为 src/manifest.json
。main
表示的是最终生成的插件目录名称。assets
则表示的插件依赖的图片等相关素材,在编译的时候会将命中该配置的文件拷贝到 <main>/Contents/Resources
目录下。web
manifest.json
这个文件你们能够理解为是 Sketch 插件的 package.json
文件。咱们来看看默认生成的 manifest.json
。
{ "$schema": "https://raw.githubusercontent.com/sketch-hq/SketchAPI/develop/docs/sketch-plugin-manifest-schema.json", "icon": "icon.png", "commands": [ { "name": "my-command", "identifier": "sketch-placeimg.my-command-identifier", "script": "./my-command.js" } ], "menu": { "title": "sketch-placeimg", "items": [ "sketch-placeimg.my-command-identifier" ] } }
看到 $schema
就有 JSON Schema 那味了,它对应的 JSON 文件地址告诉咱们能够在里面配置那些字段。其实最重要的其实就是上面列出来的 commands
和 menu
两个字段。
commands
标记了插件有哪些命令,这里只有一个命令,命令的名称(name)是 my-command
,该命令的 ID(identifier)为 sketch-placeimg.my-command-identifier
,对应的执行脚本为 ./my-command.js
。
menu
则标记了该插件的导航菜单配置,好比示例这里它指定了该插件在插件菜单中的名称(title)为 sketch-placeimg
,并拥有一个子菜单,对应的是 ID 为sketch-placeimg.my-command-identifier
的命令。经过这个 ID,菜单的行为就和执行脚本关联起来了。
manifest.json
默认的示例中有两个比较重要的字段没有配置,那就是 version
和 appcast
。version
很明显就是用来表示当前插件的版本的。而 appcast
它的值是一个 XML 的 URL 地址,该 XML 里面包含了该插件全部的版本以及该版本对应的下载地址。Sketch 会将 version
对应的版本和 appcast
对应的 XML 进行对比,若是发现有新的版本了,会使用该版本对应的下载地址下载插件,执行在线更新插件。一个 appcast.xml
文件大概是这样的格式。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"> <channel> <item> <enclosure url="https://github.com/lizheming/sketch-placeimg/releases/download/v0.1.1/sketch-placeimg.sketchplugin.zip" sparkle:version="0.1.1"/> </item> <item> <enclosure url="https://github.com/lizheming/sketch-placeimg/releases/download/v0.1.0/sketch-placeimg.sketchplugin.zip" sparkle:version="0.1.0"/> </item> </channel> </rss>
若是是经过 skpm publish
命令去发布插件的话,会自动在根目录生成一个 .appcast.xml
文件。固然按照官方文档 《Update a plugin》 所说,你也能够手动生成。
从上面的内容咱们能够知道,skpm
会经过 package.json
中指定的 manifest
文件读取全部 commands
对应的 script
文件做为编译入口文件,将这些文档编译打包输出到 <main>/Contents/Sketch
目录。全部的 assets
配置对应的文件会拷贝到 <main>/Contents/Resources
目录中。最终完成插件的生成。
换句话来讲只想要走 webpack
打包编译的话就必须是插件的命令才行。若是有一些依赖的非插件类资源,好比插件嵌入的 HTML 页面依赖的 JS 文件想要走编译的话,就须要使用 resource
这个配置了。resource
配置中配置的文件会走 webpack
的编译打包,并输出到 <main>/Contents/Resources
目录中。
一些基本原理了解清楚以后咱们就能够进行插件的开发了。首先咱们须要用户点击插件菜单以后打开一个面板,该面板能够配置尺寸、分类等基础信息。
Sketch 插件中咱们可使用原生写法进行面板的开发,可是这样写起 UI 来讲比较麻烦,并且对前端同窗来讲入门比较高。因此通常你们都会采用 WebView 加载网页的形式进行开发。原理基本上等同于移动端采用 WebView 加载网页同样,客户端调用 WebView 方法加载网页,经过实例的 webContents.executeJavaScript()
方法进行插件到网页的通讯,而网页中则使用被重定义的 window.postMessage
与插件进行通讯。
想要在插件中加载网页,须要安装 Sketch 封装好的 sketch-module-web-view
插件。
npm install sketch-module-web-view --save-dev
// src/my-command.js import BrowserWindow from 'sketch-module-web-view'; export default function() { const browserWindow = new BrowserWindow({ width: 510, height: 270, resizable: false, movable: false, alwaysOnTop: true, maximizable: false, minimizable: false }); browserWindow.loadURL(require('../resources/webview.html')) }
当你作完这些你会发现点击插件菜单后什么都没有发生,这是由于还须要更改一下配置。你们能够看到咱们最后是使用了 require()
引入了一个 HTML 文件,而官方默认的模板是没有提供 HTML 引入的支持的,因此咱们须要为 HTML 文件增长对应的 webpack loader。
咱们这里须要的是 html-loader
和 @skpm/extract-loader
两款 Loader。前者是用来解析处理 HTML 中存在的包括 <link />
或者 <img />
之类的 HTML 代码中可能存在的资源关联状况。然后者则是用来将 HTML 文件拷贝到 <main>/Contents/Resources
目录并返回对应的 file:///
格式的文件路径 URL,用来在插件中进行关联。
npm install html-loader @skpm/extract-loader --save-dev
Sketch 插件官方为咱们自定义 webpack 配置也预留好了入口,在项目根目录中建立 webpack.skpm.config.js
文件,它导出的方法接收的参数中第一个则是插件最终的 webpack 配置,咱们直接在这基础上进行修改便可。
// webpack.skpm.config.js module.exports = function (config, entry) { config.module.rules.push({ test: /\.html$/, use: [ { loader: "@skpm/extract-loader" }, { loader: "html-loader", options: { attributes: { list: [ { tag: 'img', attribute: 'src', type: 'src' }, { tag: 'link', attribute: 'href', type: 'src' } ] } } } ] }); }
html-loader
插件在新版里对配置格式作了一些修改,因此以前不少老的教程中的配置都会报错。固然若是你有更多的插件需求也能够按照这个流程往配置对象中添加。以后咱们再执行 npm run watch
,点击菜单就能够看到咱们预期的页面了。
注: 官方是提供了一套带有
sketch-module-web-view
模块的模板的,这里只是为了能更清楚的给你们解释清楚插件的原理和流程因此和他家一步一步的进行说明。真实的开发场景中建议你们直接使用如下命令进行快速初始化。skpm create <plugin-name> --template=skpm/with-webview
面板这块我准备使用 React 进行开发,主要是有 React Desktoop 这个 React 组件,可以很好的在 Web 中模拟 Mac OSX 的 UI 风格(虽然也就几个表单没什么好模拟的就是了)。
使人开心的是 skpm
默认的 webpack 配置已经增长了 React 的支持,因此咱们不须要额外的增长 webpack 的配置,只须要把 React 相关的依赖安装好就能够进行开发了。
npm install react react-dom react-desktop --save-dev
增长 webview.js
入口文件。因为该文件须要走 webpack 编译,可是又不是插件命令的执行文件,因此咱们须要像上文说的,将入口文件加入到 package.json
的 skpm.resources
配置中。
// package.json { "skpm": { "resources": [ "resources/webview.js" ] } } // resources/webview.js import React from 'react'; import ReactDOM from 'react-dom'; function App() { return (<> <p>Hello World!</p> <hr /> via: <em>@lizheming</em> </>) } ReactDOM.render(<App />, document.getElementById('app'));
webview.html
也须要改造一下,引入 JS 入口文件。这里须要注意一下 ../resource_webview.js
这个引用文件地址,这是 JS 入口文件编译后最终的文件地址。主要是由于 HTML 文件最终会生成到 <name>.sketchplugin/Resources/_webpack_resources
目录下,而 JS 入口文件会将 /
分隔符替换成 _
分隔符,生成在 <name>.sketchplugin/Resources
目录下。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8" /> <title>PlaceIMG</title> </head> <body> <div id="app"></div> <script src="../resources_webview.js"></script> </body> </html>
注:
流程打通了以后接下来咱们能够专心进行面板的开发了。面板开发这块就很少描述了,无非就是前段页面的编写而已,最后插件面板大概是长这样子的。
-_-||嗯,其实我就是想和你们讲下流程硬上 React 的…
选择完毕点击插入后,调用 postMessage()
方法将最终的配置传递给插件。
//resources/webview.js import React, {useReducer} from 'react'; function App() { const [{width, height, category, filter}, dispatch] = useReducer( (state, {type, ...payload}) => ({...state, ...payload}), {width: undefind, height: undefined, category: 'any', filter: 'none'} ); const onInsert = _ => postMessage('insert', width, height, category, filter); return ( <button onClick={onInsert}>插入</button> ); }
注:Web 原生的postMessage()
方法的语法为postMessage(message, targetOrigin, [transfer])
。事件名称和事件参数都应该序列化以后经过message
参数传入。Sketch 插件中的
postMessage()
方法是注入方法,它对原生的方法进行了复写,因此参数格式上会与原生的不同。注入方法的实现可参见sketch-module-web-view
代码。
在插件中,咱们监听 insert
事件,获取到用户选择的配置以后给生成图片图层插入到画板中。
//src/my-command.js import sketch, { Image, Rectangle } from 'sketch/dom'; import BrowserWindow from 'sketch-module-web-view'; export default function() { const browserWindow = new BrowserWindow({...}); browserWindow.webContents.on('insert', function(width, height, category, filter) { const url = 'https://placeimg.com/' + [width, height, category, filter].join('/'); new Image({ image: NSURL.URLWithString(url), parent: getSelectedArtboard(), frame: new Rectangle(0, 0, width, height), }); return browserWindow.close(); }); }
最终咱们的插件的主体功能就开发完毕了。下面咱们就能够进行插件的发布了。咱们能够直接使用 skpm publish
进行发布,它须要你经过 skpm publish --repo-url
或者是 package.json
中的 repository
字段为插件指定 Github 仓库地址。
在 Personal Access Token 页面为 skpm 申请新的 Token,记得勾选上 repo
操做的权限。使用 skpm login <token>
进行登陆以后,skpm 就得到了操做项目的权限。
最后经过 skpm publish <version>
就能够成功发布了。如前文所说,发布后会在项目目录建立 .appcast.xml
文件,同时会发布一条对应版本的 Release 记录,提供插件的 zip 包下载地址。执行完 publish 操做后,若是发现你的插件尚未在插件中心仓库中列出来,还会询问你是否提交个 PR 把本身的插件增长上。
固然若是你的插件不方便发布到 Github 上,也可使用前文所说的手工发布,执行 skpm build
后对生成的 <name>.sketchplugin
目录进行打包便可。
上文的示例插件比较简单,因此没有使用特别多的调试手段。在官方教程《Debug a plugin》中描述了多种能够进行调试的方式。用的比较多的仍是日志调试方式,可使用系统的 Console.app
查看日志,也可使用 skpm log -f
插件日志。
文档里说的大部分是插件的调试,WebView 内的前端代码调试会更简单一点。WebView 窗体右键审查元素便可使用 Safari 的开发者工具进行调试了。
注:插件自己的代码本质是客户端代码,WebView 本质是前端代码,因此二者的调试和日志输出位置都是有区别的,这里要注意区分。
以上就是开发 Sketch 的一些基础知识和简单流程,其它的就是多去看一下 Sketch API 文档了。不过在实际的使用中 Sketch 的这套 JavaScript API 并非很是完美,部分功能可能还暂时须要使用原生 API 区别。这时候能够多 Google 一下,能找到不少前人的实现,节省本身的工做量。
本文主要是介绍了一套 JavaScript API + WebView 的偏前端的开发方式,代码我都已经放到 Github 上 https://github.com/lizheming/...,你们能够自行查阅和下载。除了这种方式以外,咱们也可使用 OC + WebView 甚至是纯 OC 客户端的方式去开发插件。使用纯客户端开发的话性能会比 JavaScript API 的形式好一点,可是对于不了解 OC 开发的前端同窗来讲上手难度仍是比较高的。
除了 Sketch 以外,Figma 也是一款很是棒的 UI 设计软件。它基于 Web 开发,天生跨平台,更提供了更加易用的协做模式,解决 UI 开发中的多人协做问题。感兴趣的同窗也能够去了解一下。
参考资料: