如何製做 Sketch 插件

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 初始化項目

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

package.json

和大多數 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.jsonmain 表示的是最終生成的插件目錄名稱。assets 則表示的插件依賴的圖片等相關素材,在編譯的時候會將命中該配置的文件拷貝到 <main>/Contents/Resources 目錄下。web

manifest.json

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 文件地址告訴咱們能夠在裏面配置那些字段。其實最重要的其實就是上面列出來的 commandsmenu 兩個字段。

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,菜單的行爲就和執行腳本關聯起來了。

appcast.xml

manifest.json 默認的示例中有兩個比較重要的字段沒有配置,那就是 versionappcastversion 很明顯就是用來表示當前插件的版本的。而 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》 所說,你也能夠手動生成。

resource

從上面的內容咱們能夠知道,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-module-web-view

想要在插件中加載網頁,須要安裝 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 進行開發,主要是有 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.jsonskpm.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>

注:

  1. HTML 文件生成到 _webpack_resources 配置
  2. JS 入口文件生成到 Resource 目錄配置

面板開發

流程打通了以後接下來咱們能夠專心進行面板的開發了。面板開發這塊就很少描述了,無非就是前段頁面的編寫而已,最後插件面板大概是長這樣子的。

-_-||嗯,其實我就是想和你們講下流程硬上 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 開發中的多人協做問題。感興趣的同窗也能夠去了解一下。

參考資料:

  1. 《Sketch插件開發總結》
相關文章
相關標籤/搜索