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 開發中的多人協做問題。感興趣的同窗也能夠去了解一下。
參考資料: