最近做 h5 页面重构的时候,遇上几个让我很难受的问题:

  1. 为了设配,h5 通常会使用 rem 单位,几乎所有尺寸单位,都需要手动写一遍,px2rem($pixel);
  2. 对于颜色,阴影这些属性,每次都得手动加 #, 或者 rgba;
  3. 对于一些不常用的属性,得手动查文档,比如 box-shadow linear-gradient;
  4. 对于一些不是很明显的样式,经常会出现错漏,比如 text-shadow;

这些都是小问题,然而每次 h5 开发都会遇上,影响时间,影响效率,影响心情。有没有办法可以优化呢?围绕着这些问题,我开始尝试 Sketch 插件开发。

开发环境

开发过程需要经常打印日志调试,可以通过 document.showMessage 直接答应在 Sketch 页面内,还可以通过 Console 软件,设置进程,库都为 Sketch,来查看日志打印,然后代码里面通过 log 或者 print方法写入log

console

Sketch 默认不会自动更新脚本代码,如果你希望每次保存Sketch自动更新,可以执行以下代码。

1
defaults write ~/Library/Preferences/com.bohemiancoding.sketch3.plist AlwaysReloadScript -bool YES

插件简介

Sketch 插件都是 *.sketchplugin 的形式,其实就是一个文件夹,通过右键显示包内容,可以看到最普通的内部结构式是这样的:
代码结构

manifest.json 用来声明插件配置信息,commands 定义所有可执行命令,每条 command 有唯一标志符,identifier,menu 定义插件菜单,通过 identifier 关联到执行命令。handlers.actions 下面可以配置需要监听的事件,Sketch 会在事件被触发时执行相应的事件方法,可以在 Action List 这里看到所有的 Action 或者装一个 Action-Logger 插件可以看到事件什么时候触发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"name" : "first-plugin",
"identifier" : "com.w3ctrain",
"version" : "1.0",
"description" : "first plugin",
"author" : "w3ctrain",
"commands" : [
{
"script" : "plugin.js",
"handlers": {
"run": "onRun",
"actions": {
"SelectionChanged.finish": "onSelectionChanged"
}
},
"name": "Say Hello World",
"identifier" : "com.w3ctrain.HelloWorld"
}
],
"menu" : {
"items" : [
"com.w3ctrain.HelloWorld"
],
"title" : "first plugin"
}
}

plugin.js 对应 manifest.json script 字段的执行脚本,这样的脚本可以有很多个,也可以随意命名。

1
2
3
4
5
6
7
8
9
10
11
12
var onRun = function (context) {
context.document.showMessage("Hello World")
}

var onSelectionChanged = function (context) {
var selection = context.actionContext.document.selectedLayers().layers();
if (selection.length >= 1) {
context.actionContext.document.showMessage(`${selection.length} layers selected`)
} else {
context.actionContext.document.showMessage("nothing selected")
}
}

把插件放到 ~/Library/Application Support/com.bohemiancoding.sketch3/Plugins/ 目录下,就可以在 Plugins 菜单中执行。

Hello World

Sketch 插件是使用 CocoaScript 编写,放心不是什么新语言,其实就是 JavaScript + ObjectC 宿主环境,也就是说你可以写 JS 来调用 ObjectC 的方法和对象。

比如

1
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString: @"https://baidu.com"]]]

用 CocoaScript 写起来时这样的:

1
webView.loadRequest(NSURLRequest.requestWithURL(NSURL. URLWithString("https://baidu.com"))

实际上我并不关心 ObjectC 代码,我只是个前端,只要能完成创建一个 Panel 加载我的网页就行。下一个要解决的问题是 Sketch 事件触发的时候如何通知 webview 更新,其实很简单,在之前的文章中就有遇到过,ObjectC 是可以直接调用 JS 代码的。

Sketch 通知 webview

webView.js 暴露全局方法

1
2
3
function updatePreview () {
// 拿到 css attributes
}

plugin.js,通过 evaluateWebScript 调用全局方法

1
2
3
4
5
6
7
8
9
function onSelectionChanged {
var windowObject = webView.windowScriptObject();
var selection = context.actionContext.document.selectedLayers().layers();
if (selection.length >= 1) {
windowObject.evaluateWebScript(`updatePreview(${ayer.CSSAttributes().join('')})`);
} else {
windowObject.evaluateWebScript("updatePreview(' ')");
}
}

由于 Sketch 提供的 cssAttributes 属性并不是我们最终想要的,所以需要对这堆内容进行补充替换截取处理。

其他处理

补充文本对齐和尺寸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let layer = selection[0];
var frame = layer.frame();
var attributes = layer.CSSAttributes();

if (layer.class() == "MSTextLayer") {
if (!layer.lineHeight()) {
attributes.addObject(`line-height: 1.4;`);
}
for (const key in TEXT_ALIGNMENT) {
if (layer.textAlignment() === TEXT_ALIGNMENT[key]) {
attributes.addObject(`text-align: ${TEXT_ALIGNMENT_MAP[key]};`);
}
}
}
attributes.addObject('\n');
attributes.addObject('/* Size: */');
attributes.addObject(`width: ${frame.width()}px;`);
attributes.addObject(`height: ${frame.height()}px;`);

过滤掉不常用的属性。

1
2
3
4
5
6
7
function removeBlackListAttributes (codeString) {
var blackList = ['font-family', 'letter-spacing']
for (let i = 0; i < blackList.length; i ++) {
codeString = codeString.replace(new RegExp(`${blackList[i]}(.*)\n`, 'ig', ''), '');
}
return codeString
}

进行单位替换

1
2
3
4
5
6
7
8
9
10
  var unitsFunc = (match, val) => {
let unit = unitInput.value
let miniPixel = miniPixelInput.value
if (val <= parseInt(miniPixel)) {
return `${val}px`
}
return `${unit}(${val})`
}
let result = codeString.replace(/([0-9]+)px/ig, unitsFunc)
}

一顿操作之后看到的效果是这样的:

效果

实现起来比预期的开发时间要少很多,因为很多东西不需要自己处理,然而整体效果是非常显著的,文章一开头提到的通点都解决了,复制粘贴即可,动动手写几行代码就能解决自己的需求,开心。

具体的代码可以看这里

相关文章

如需转载,请注明出处: http://w3ctrain.com / 2017/12/07/sketch-plugin-development/

helkyle

我叫周晓楷

我现在是一名前端开发工程师,在编程的路上我还是个菜鸟,w3ctrain 是我用来记录学习和成长的地方。