最近做 h5 页面重构的时候,遇上几个让我很难受的问题:
这些都是小问题,然而每次 h5 开发都会遇上,影响时间,影响效率,影响心情。有没有办法可以优化呢?围绕着这些问题,我开始尝试 Sketch 插件开发。
开发过程需要经常打印日志调试,可以通过 document.showMessage 直接答应在 Sketch 页面内,还可以通过 Console 软件,设置进程,库都为 Sketch,来查看日志打印,然后代码里面通过 log 或者 print方法写入log
console
Sketch 默认不会自动更新脚本代码,如果你希望每次保存Sketch自动更新,可以执行以下代码。
- 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 插件可以看到事件什么时候触发。
- {
- "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 字段的执行脚本,这样的脚本可以有很多个,也可以随意命名。
- 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 的方法和对象。
比如
- [webView loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString: @"https://baidu.com"]]]
用 CocoaScript 写起来时这样的:
- webView.loadRequest(NSURLRequest.requestWithURL(NSURL.URLWithString("https://baidu.com"))
实际上我并不关心 ObjectC 代码,我只是个前端,只要能完成创建一个 Panel 加载我的网页就行。下一个要解决的问题是 Sketch 事件触发的时候如何通知 webview 更新,其实很简单,在之前的文章中就有遇到过,ObjectC 是可以直接调用 JS 代码的。
webView.js 暴露全局方法
- function updatePreview() {
- // 拿到 CSS attributes
- }
plugin.js,通过 evaluateWebScript 调用全局方法
- 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 属性并不是我们最终想要的,所以需要对这堆内容进行补充替换截取处理。
补充文本对齐和尺寸
- 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;`);
过滤掉不常用的属性。
- 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
- }
进行单位替换
- 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)
- }
一顿操作之后看到的效果是这样的:
效果
实现起来比预期的开发时间要少很多,因为很多东西不需要自己处理,然而整体效果是非常显著的,文章一开头提到的通点都解决了,复制粘贴即可,动动手写几行代码就能解决自己的需求,开心。
具体的代码可以看这里。
来源: https://juejin.im/entry/5a2946106fb9a045117107c2