sketch 插件开发实战
最近入坑了 sketch, 之前以为 sketch 只是 APP 效果图设计工具,后来发现身边好多研发朋友也在用,向设计师朋友请教了一下,原来 sketch 的修图在一定程度上能替代 PhotoShop , 做高逼格的流程图、效果图也很方便。以前真是 too young、too naive。
工作上设计经常变动,类似于换肤的项目也有很多,每次都是设计狮在 sketch 里设计好效果图,上传到蓝狐给产品审阅,然后在切图,导出,压缩,传给前端,前端在代码里替换,然后在 webpack 打包的的时候上传到 cdn。刚开始觉得流程没有什么问题,后来发现环节一多,越容易出错。研发里面讲高耦合,低内聚,把数据和视图分开,为什么设计和研发要这么纠缠在一起,设计改一个图,也要经过研发这边修改、提交 Git、部署上线这一繁琐的流程。好不科学,其实可以借鉴蓝狐的思路,设计直接在 sketch 里将图上传到 CDN,然后更新一下图片地址,岂不美哉。
这就需要做一个精简版蓝狐插件。好在 sketch 有简易的上手指南。
- 创建新插件
nmp install -g skpm
skpm create my-plugin
cd my-plugin
- 运行插件
npm run watch
然后打开sketch,我滴乖乖,居然这么简单,居然在 Plugin 里面直接就有了我们刚刚创建的 my-plugin,点击里面的 My Command,直接弹出了toast 提示。
然后自信心爆棚,javascript 嘛,分分钟搞一个蓝狐插件出来。接着就懵逼了。按照需求无非就是两步,第一获取到图片,第二上传。一清晰思路,这才发现事情并没有这么简单,这里用到的语言根本不是我想象的javascript,只是 JavaScript 标准库,也就是说语法是可以用的,但是 setTimeout、fetch、console 都是不能用的,NodeJS 核心组件也是不能用的,好家伙,阉割版 JavaScript。还好sketch 帮忙填了一些坑,使用 skpm 创建的插件,是可以使用 console、setTimeout、setInterval、fetch 的。各种UI 界面还要用到 Mocha 和 CocoaScript,Mocha 和 CocoaScript 是通过 JavaScript 调用Objective-C/Cocoa 代码的桥梁库。一看 Mocha 和 CocoaScript,文档给的很少,稍微详细一点的是 JavaScript API/Action,唉,Objective-C/Cocoa 完全不会啊,对照着文档开发几乎不可能,要凉。
继续研究,看my-plugin 的代码,经过一番鼓捣,终于靠 JavaScript 搞定了,代码如下
my-command.js
const Settings = require('sketch/settings');
const UI = require('sketch/ui');
export function uploadSelect(context) {
onRun(context, false);
}
export function setting(e) {
onRun(context, true);
}
function exportLayerAsBitmap(document, layer, scale) {
var slice,
result = {},
rect = layer.absoluteRect(),
path = NSTemporaryDirectory() + layer.objectID() + '.png';
NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()),
(slice = MSExportRequest.exportRequestsFromExportableLayer(
layer
).firstObject()),
(slice.page = document.currentPage()),
(slice.format = 'png'),
(slice.scale = scale),
document.saveArtboardOrSlice_toFile(slice, path);
var url = NSURL.fileURLWithPath(path),
bitmap = NSData.alloc().initWithContentsOfURL(url),
base64 = bitmap.base64EncodedStringWithOptions(0);
NSFileManager.defaultManager().removeItemAtURL_error(url, nil);
var imgRep = NSBitmapImageRep.imageRepWithData(bitmap);
return (
(result.bitmap = base64),
(result.width = imgRep.pixelsWide() / 4),
(result.height = imgRep.pixelsHigh() / 4),
result
);
}
const onRun = function(context, setting) {
const url = Settings.settingForKey('url');
if (setting || !url) {
UI.getInputFromUser(
'Input the server address.',
{
initialValue: url || 'https://'
},
(err, value) => {
if (err) {
return;
}
return Settings.setSettingForKey('url', value);
}
);
return ;
}
const doc = context.document;
const selection = context.selection;
if (selection.count() == 0) {
doc.showMessage('Please select the layer to upload.');
} else {
for (let i = 0; i < selection.count(); i++) {
const layer = selection[i];
const layerName = layer.name();
const { bitmap } = exportLayerAsBitmap(doc, layer, 1);
fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: { image: bitmap, name: layerName }
})
.then(response => response.text())
.then(text => doc.showMessage(text))
.catch(e => doc.showMessage(e.message));
}
}
};
manifest.json
{
"compatibleVersion": 3,
"name": "Snatch",
"description": "Snatch, upload layer to cdn directly, improve work efficiency.",
"author": "chenzhicheng.com",
"version": "0.1.0",
"identifier": "com.chenzhicheng.snatch",
"bundleVersion": 1,
"icon": "icon.png",
"commands": [
{
"name": "Upload Select",
"identifier": "snaptchSelect",
"shortcut": "ctrl command p",
"script": "./my-command.js",
"handler" : "uploadSelect"
},
{
"script" : "./my-command.js",
"handler" : "setting",
"name" : "Set Server URL",
"identifier" : "serverUrl"
}
],
"menu": {
"title": "Snatch",
"items": [
"snaptchSelect",
"-",
"serverUrl"
]
}
}
主要包含两个功能,一个是点击上传选中的 layer,一个是点击设置弹出 prompt,输入服务器地址,可以上传到指定的地址。
其中发现了开发插件的两个好东西,一个就是Sketch-dev-tool,另一个就是Run Script。Sketch Dev Tool 本身就是一个 Sketch 插件,类似于 chrome 的 inspect,可以查看调试信息。Run Script 是一个实时运行 JavaScript 的地方,在 Plugin -> Run Script 里打开。
为了测试上传,还要有服务器端,这个就简单了,用 ExpressJS 新建一个好了。
- 创建服务文件夹
mkdir expressjs
cd expressjs
npm init
一路默认
- 安装express 及依赖
npm install express
npm install body-parser
npm install sharp
- 创建一个 index.js 文件,内容如下
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const sharp = require('sharp');
app.use(bodyParser.urlencoded({extended: false, limit: '50mb'}))
app.use(bodyParser.json({limit:'50mb'}))
const boostSize = {
'pic_wheel': [353, 353, 'png'],
'pic_arrow': [45, 60, 'png'],
'btn_luckydraw': [292, 77, 'png'],
'bg_blue': [360, 640, 'jpg']
}
app.post('/', function(req, res) {
const size = boostSize[req.body.name];
if (!size) {
return res.send('Name invalid!');
}
let buff = new Buffer(req.body.image, 'base64');
let resize = sharp(buff)
.resize(size[0], size[1]);
if (size[2]=='jpg') {
resize = resize.jpeg();
} else {
resize = resize.png({adaptiveFiltering: true})
}
resize.toFile(req.body.name+'.'+size[2], (err) => {
if (err) {
return res.send(err);
} else {
return res.send('Upload Success!');
}
});
});
app.listen(3000, function(){
console.log('ok.');
});
- 运行
node index.js
这样,选中图层,点击Plugin -> my-plugin -> my-command,则会上传图片到server 文件夹下。
最后一步需要将插件发布到官方,有两种方法,一种是forkPlugin directory,然后提交 PR,另一种是使用 skpm publish。我使用 skpm publish 失败了。
最后附上 github 链接