如何解析出令人惊艳的Psd设计稿?

2023/05/18 09:47
阅读数 428

1

起源

一次和设计同学的闲聊中, 了解到他们的设计稿交付时,大多数都是基于sketch,而Psd的设计稿非常少,使得他们一身Psd技艺无用武之地。 究其原因,是因为sketch对css样式支持度较高,转换到HTML元素相对容易,但Psd并无一一映射规则,且Psd的样式繁杂,解析起来相当麻烦。  鲁迅先生说过: “即使艰难,也还要做; 愈艰难,就愈要做”。 打了一番鸡血,几位小伙伴一拍即合,便开始去做这件“艰难”的事情——将Psd设计稿转译成HTML。

当然我们也了解过业界的类似产品,目前psd解析市面产品均停留在切图/标注方面;其余产品均是基于psd.js的衍生产品,由于此开源项目本身解析元素不够全面,解析效果均不尽如人意,这也是我们开始做的原因。


2

总体设计


如上图,web服务层进行了源文件的上传;
在解析服务层,psd文件工具解析后进行了图层重构、属性解析;
最后在web服务生成页面布局,页面展示,预览下载,并进行了文件存储。


3

解析服务

3.1 psd解析引擎方案对比

我们去调研了市面上的几种psd的解析引擎,发现比较不错的有两个,为了更高的还原性,进行了对比

主要从以下几个方面,通过表格可以看出ag-psd都是优于psd.js的

解析插件名称 psd.js ag-psd
解析依据 psd.rb解析器 photoshop官方文档
star数 2.5k 297
更新频率 去年 上个月
issue回复速度 周甚至月 天级
图像模式支持度 深度和图像模式有限 几乎所有
遍历方式 插件API方法 自由操作原始数据
颜色模式 rgba rgba
图片操作方式 转png 支持canvas所有API
转换css属性 属性有限 大量属性可转换
解析数据(以6.8MB的Psd文稿为例) 135KB 34.1MB
解析成功率 重名图层会出现卡死/报错 100%

结果一目了然,除star数稍逊色之外,其他方面ag-psd一骑绝尘,选定插件,开始搬砖。

3.2 图层重构


第一步:图层分类

目前图层可分为三类:组(group),文字(text),图片(image)。其中组图层有children属性,它是可以包含所有类型图层的列表;文字图层也拥有children属性,但它仅能包含文字图层。

// 图层导出类型export enum LayerShowType {  GROUP  = 'group',  TEXT = 'text',  IMAGE = 'image',}

第二步:过滤隐藏图层

对图层中带有以下几类的图层删除元素,避免了无效元素对后续布局的影响

“opacity”: 0 ----- 删除元素
“hidden”: false ----- 删除元素
“width”:0 ----- 删除元素

第三步:位置处理

由于Psd源数据本身就是绝对定位,最简单高效的方式是沿用它。所以我们直接使用position: absolute的样式,将图层属性top, right, bottom, left直接转换。

// 位置关系export type Location = {  left?: number;  top?: number;  right?: number;  bottom?: number}

第四步:结构简化

递归去掉无效分组,输出内容有效子元素;图层拍平,简化结构

const flatLayerT = (layerData: LayerT) => {  const { name, location, width,    height, StyleT, image,    id, textT } = layerData;  const { left, right, top, bottom } = location;
return { id, name, left, right, top, bottom, width, height, opacity: StyleT?.opacity, dataURL: image?.dataURL, textT, };};

3.3 属性解析

属性解析主要是将psd源文件中的属性一一对应成CSS样式,这样就得到了细节的视觉效果。
解析得到的是接近Psd源格式的数据,要想将其转换为HTML文件,首要任务是将Psd属性“翻译”成css样式。但Psd的样式无法和css样式一一对应,需要做转换映射

第一步:提取公有属性

  width: number;  height: number;  id?: number;  children?: LayerT[];  hidden?: boolean;  opacity?: number;  borderRadius?: string;  backgroundColor?: string;

第二步:归类私有属性

// 文字class Text {  fontFamily =  '';  fontSize = 0;  textDecorationLine?: TextDecorationLine[];  color = '';  fontWeight: FontWeight = FontWeight.NORMAL;  text = '';  styleRuns? :TextStyleRun[]; // 一行文字的多个样式  lineHeight = 0;  textAlign: AlignMent = AlignMent.LEFT;  textIndent = 0;  fontStyle: FontStyle = FontStyle.NORMAL;}// 图片class Image {  dataURL: string;}

第三步:文字属性转换

其中,常见的opacity(透明度),borderRadius(圆角),backgroundColor(背景色)等样式是通用样式,也很容易找到对应属性,但文字就相对难处理一些。

诸如删除线、下划线,加粗,倾斜等样式,Psd属性中是以Boolean形式存在;行高属性在Psd中是leading,单位是Psd文稿定义的单位;圆角在Psd中以Object存储,等等;而css样式中,是以单独样式及固定值存在,这里都需要转换映射。

// 文字基本样式      "style": {          "font": {            "name": "FZSKJW--GB1-0",            "script": 3,            "type": 1,            "synthetic": 2          },          "fontSize": 60,          "fauxBold": true,          "autoLeading": false,          "leading": 72,          "tracking": 0,          "autoKerning": true,          "kerning": 0,          "fillColor": {            "r": 9.00405,            "g": 0,            "b": 137.0013,            "a": 1          },          "strokeColor": {            "r": 255,            "g": 255,            "b": 255,            "a": 1          },          "hindiNumbers": false        }
除了普通的文字样式,特殊的多行样式处理也需要处理

如下图所示,2种不同的样式糅合在一个文本图层中,需要从图层的TextStyleRun列表中,将各自样式单独拆离出来,再做转换映射。

   
   
   
 // Psd解析原始数据 "styleRuns": [     {       "length": 1,       "style": {         "fontSize": 24,         "autoKerning": false,         "fillColor": {           "r": 225.99885,           "g": 61.15665,           "b": 61.15665,           "a": 1         }       }     },     {       "length": 69,       "style": {         "fontSize": 24,         "autoKerning": true,         "fillColor": {           "r": 225.99885,           "g": 61.15665,           "b": 61.15665,           "a": 1         }       }     },     {       "length": 88,       "style": {         "fontSize": 48,         "autoKerning": true,         "fillColor": {           "r": 26.9994,           "g": 23.715,           "b": 23.715,           "a": 1         }       }     },     {       "length": 39,       "style": {         "fontSize": 36,         "autoKerning": true,         "fillColor": {            "r": 129.999,           "g": 46.39215,           "b": 46.39215,           "a": 1         }       }     } ]
第四步:图片转换

考虑到时间及转化难度的问题,目前的图片分为两部分:设计稿中本身的图片图层及非文字图层。由于ag-psd基于canvas处理图片,此处借用canvas的toDataURL方法,将所有图片统一转换成base64格式来存储备用。

class Image {  dataURL: string;
constructor(canvas: HTMLCanvasElement) { this.dataURL = canvas.toDataURL(); }}

至此,我们在解析服务中得到了一个包含页面重构所需的完整信息的json,并生成npm包,用于web服务调用


4

web服务

web服务是以psd解析服务做为基础,通过调用解析服务把psd文件进行处理,随后把htmlContent返回结果存储在静态服务器,以便前端进行预览及下载


HTML文件拼接

准备就绪,开始拼接HTML元素。但因为场景在node端,无法直接使用Document.createElement来创建DOM,更无法直接添加css样式。
几经寻找对比,终于找到一款在node端操作DOM的包——
cheerio,它提供了在node端操作DOM的一套API,是jQuery的子集。有了它,何愁不能拼接完整的HTML,想到这里,不禁沾沾自喜。

拼接分为如下3个步骤:

1. 数据解析

利用上述结论,将原Psd数据进行解析转换,将图层类型转换成固定三类(组,文字,图片),同时将样式转换成对应图层类型的css属性。

2. 递归处理

对转换后的数据进行递归处理,使用 cheerio 的API,对 组(group) 图层,新建div元素;对 文字(text) 图层,新建p元素;对 图片(image) 图层,新建img元素。再通过attr及css方法把对应的css样式添加到DOM元素中。

  $imgEle(`#${id}`).attr('src', dataURL);
$imgEle(`#${id}`).attr('name', name);
$imgEle(`#${id}`).css('position', 'absolute');
$imgEle(`#${id}`).css('left', `${left * ratio}${unit}`);
$imgEle(`#${id}`).css('right', `${right * ratio}${unit}`);
$imgEle(`#${id}`).css('top', `${top * ratio}${unit}`);
$imgEle(`#${id}`).css('bottom', `${bottom * ratio}${unit}`);
$imgEle(`#${id}`).css('width', `${width * ratio}${unit}`);
$imgEle(`#${id}`).css('height', `${height * ratio}${unit}`);
$imgEle(`#${id}`).css('opacity', String(opacity));

3. 单位变换

本次解析是基于移动端设计稿处理,但由于移动端屏幕尺寸多种多样,px单位是无法在HTML中直接使用。考虑到大部分移动端设计稿是基于750px宽度来定义尺寸,此处均将px转换为rem单位,同时定义基准fontSize = 100px。
由于单位转换后,文字样式的宽度会有一定差异性,会出现折行问题。经过多次尝试,基于rem单位,我们会给文字添加一定数值的宽度补偿,最终展示的文字能够完美适配原设计稿。


     
     
     
let newWidth = (Math.ceil(width / 10) * 10 * ratio + 0.3) + unit;


5

文件储存&结果展示

上传至wos,可直接在线预览

最终,我们得到了这样的结果:用户中心设计稿 ,还原度还不错,粗略估计在85%以上。

原设计稿:


转换后页面效果(在线设计稿地址 ):


6

未来规划

目前Psd设计稿的还原度并非最理想状态,同时仅还原出HTML也不是我们的目的。未来有如下的规划:

  • 大幅提升设计稿的还原度

  • 完成Psd在线设计稿功能开发,方便前端开发者使用

  • 支持在风火轮平台实现psd标注功能

如何体验

进入毕加索官网(点击原文访问),点击右上角解析PSD 按钮即可体验Psd设计稿转换。
同时,也欢迎大家关注58开源项目 
Picasso ,体验效果、改进代码、协作开发都可以提到issue中。

本文分享自微信公众号 - 58技术(architects_58)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部