先看几张PSD海报
《中秋节祝福海报》
PSD转H5演示视频
需求场景
- 中秋节将近,某点评App,需要在其App内部做一个活动页面,向用户宣传一下中秋节的一些活动
- 教师节将近,某校园App,需要在其App上做一个活动页面,大致的意思是 希望表达对老师的崇敬之情
- 最近一段时间,房地产市场火爆,某房产中介正在招兵买马,希望在其App上或者朋友圈发一个招聘H5
以前的模式
- 设计师同学用 Photoshop 做好了类似下图的一张 PSD,然后再使用SKetch 进行标注。准备完毕之后,将相关的文件发给前端同学。
- 前端同学拿到 Sketch 和 PSD 之后,开始按照标注进行制作H5页面,经过一到两天的静态页面开发(具体情况具体分析,具体的开发周期取决于页面的复杂度),期间也和设计师、产品经理同学进行了协商,最终还原了设计稿,双方都觉得比较满意,觉得可以上线了。
- 前端同学 把图片 + 代码 上传到服务器,配置一个域名之后,正式上线。大家就可以通过 h5.xxx.com 访问到一个中秋节H5页面了
现在 在鲁班H5中,这么做就可以了:
- 设计师同学只要打开鲁班的编辑器
- 点击图中的
Ps(导入PSD) - 在弹出的页面中,上传需要制作成H5的PSD 文件即可
- 大部分情况下,不到一分钟,一个和 PSD 长得几乎一样的 H5 页面就会出现在中间画布上了(具体时间取决于psd 文件大小和图层多少)
- 点击预览,设计师再做一些细节的调整,点击发布,会得到一个 该H5作品对应的网址,制作完成!
- 我们发现,上述流程比之前工程师写代码的流程差不多节约了 三到四天的时间

实现原理
基础认知
- 我们的目的是:PSD -> H5
根据我们之前的讲解,以《中秋节祝福海报》为例,我们知道一个简单的H5本质上对应的是一个 JSON,如下所述(其中:lbp意思是 lu-ban-plugin):
[{name: '兔子图片',type: 'lbp-image'commonStyle: {top: '100px',left: '120px'}},{name: '英文祝福文字',type: 'lbp-text'text: 'Dear Friends',commonStyle: {top: '240px',left: '160px'}}]
- 那么
PSD -> H5也就意味着,我们需要把 PSD 文件转换为上面的JSON结构,我们大概看下: - 假如我们有某种办法,能够得到PSD中的图层结构,能够对 PSD 文件做以下操作:
- 把每个图层中的图片保存下来
- 获得该图层的
左上角相对于整个PSD 的相对左上角的位置(以左上角为坐标原点,向右为X正轴,向下为Y正轴),也就是:{left, top} - 获得文字图层中的文字内容、文字大小、颜色、字体等信息
- 再高级点:图层的渐变色等
- 如果上面的办法可行,我们可以把 PSD 文件也抽象成一个 JSON(如下)
[{name: '兔子图片图层',type: 'image',position: {top: '100px',left: '120px'},},{name: '左灯笼图片图层',type: 'image',position: {top: '100px',left: '120px'},},{name: '右灯笼图片图层',type: 'image',position: {top: '100px',left: '120px'},},{name: '英文祝福文字图层',type: 'text',text: 'Dear Friend',position: {top: '240px',left: '160px'},}]
殊途同归
可以发现,PSD 文件的数据结构和鲁班中的一个作品的数据结构是非常相似的
我们来整理一下二者之间相似之处(如下),是不是发现PSD 的结构和鲁班在很多地方是可以一一对应的上的呢?
image(psd) <-> lbp-picture(luban)text (psd) <-> lbp-text(luban)
实现路径
- 那么让我们看一下我们之前的假设:
假设我们可以分析PSD的结构,并从中得到图片、文字等图层信息 - 现在可以告诉大家了,上面的假设,我们完全可以实现。
- 首先,让我们重新认识一下PSD这个文件格式,通常情况下,我们都知道它是Photoshop导出的文件格式。其实它也是一个有着自己独特风格的文件格式。
- 我们先来看一份东西: PSD 的文件规范,访问地址:https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/
看一下 PSD文件结构,有一种被计算机网络七层结构支配的恐惧😂
也就是说 PSD 文件是由几个部分组成的:
- File Header
- Color
- Image Resources
- Layer And Mask Information
- Image Data
我们借助 psd.js 来尝试下对 psd 文件进行一些分析,代码如下,可以copy 运行(前提,需要有一个 psd 文件,拷贝到该脚本的相同目录中) ```javascript
mkdir demo
将 psd 文件拷贝到 demo 文件夹中
yarn add psd
var path = require(‘path’) var PSD = require(‘psd’); var psd = PSD.fromFile(path.resolve(__dirname, ‘your_psd_file_name.psd’)); psd.parse();
就可以看到如下的数据结构了
console.log(psd.tree());
```javascriptroot {psd: PSD {file: File {data:<Buffer 38 4....> ,pos: 7577696},parsed: true,header: Header {file: [File],sig: '8BPS',version: 1,channels: 4,rows: 900,cols: 600,depth: 8,mode: 3},resources: LazyExecute {obj: [Resources],file: [File],startPos: 30,loaded: false,loadMethod: 'parse',loadArgs: [],passthru: []},layerMask: LazyExecute {obj: [LayerMask],file: [File],startPos: 28884,loaded: true,loadMethod: 'parse',loadArgs: [],passthru: []},image: LazyExecute {obj: [Image],file: [File],startPos: 7577696,loaded: false,loadMethod: 'parse',loadArgs: [],passthru: []}},layer: {name: null,left: 0,right: 600,top: 0,bottom: 900,height: null,width: null,node: [Circular]},parent: null,_children: [Group {layer: [Layer],parent: [Circular],_children: [Array],name: '站长素材(sc.chinaz.com)',forceVisible: null,coords: [Object],topOffset: 0,leftOffset: 0}],name: null,forceVisible: null,coords: {top: 0,bottom: 900,left: 0,right: 600},topOffset: 0,leftOffset: 0}
我们来对上面的数据结构进行一些简单的分析,重点看一下其中的 root.psd ,也就是下面的数据结构。
其中 file:File 类似 TypeScript 的写法,表示 file 变脸的类型是 File。LazyExecute 类似于迭代器的感觉,用的时候,执行一下才能得到结果,也就是 realResource = root.psd.resource() 类似这种感觉
{file: File {data:<Buffer 38... >,pos: 7577696},parsed: true,header: Header {file: [File],sig: '8BPS',version: 1,channels: 4,rows: 900,cols: 600,depth: 8,mode: 3},resources: LazyExecute {obj: [Resources],file: [File],startPos: 30,loaded: false,loadMethod: 'parse',loadArgs: [],passthru: []},layerMask: LazyExecute {obj: [LayerMask],file: [File],startPos: 28884,loaded: true,loadMethod: 'parse',loadArgs: [],passthru: []},image: LazyExecute {obj: [Image],file: [File],startPos: 7577696,loaded: false,loadMethod: 'parse',loadArgs: [],passthru: []}}
我们对其进行进一步的整理,发现它其实可以整理为下面的这种结构
root.psd: {file<File>: {},parsed: true,header<Header> : {},resources<LazyExecute>: {},layerMask<LazyExecute>: {},image<LazyExecute>: {}}
我们仔细看一下,上面的数据结构 是不是和 PSD 文件规范 里面描述的 Photoshop file Structure 能够一一匹配上了。
到这里相信大家应该能够明白了吧,我们通过程序(主要借助 psd.js)发现可以对 PSD 文件进行解读,从中得到我们想要的数据。接下来,我们对 Photoshop file Structure进行略微深入一些的解读
Photoshop file Structure 解读
- 在这里,我就不班门弄斧了,大家感兴趣的话,可以看看这位小哥iamgqb 翻译的PSD 文件格式规范/Adobe Photoshop File Formats Specification. 再次表示感谢
- 我会补充一些说明,也就是对规范中的一些字段在实际有何用途
1. File Header: 文件头段包括图像的基本属性
大家可以先读一下 译文,以下内容大部分来自译文。 补充部分,为我个人理解
长度 描述 补充 4 Signature(签名): 总是等于 ‘8BPS’ 。 不要去读取签名不符合这个值的文件。 用户在上传文件的时候,可以根据 签名 判断该文件是否是合法的PSD文件 2 Version(版本): 总是等于 1。不要去读取版本不符合这个值的文件。 (PSB 版本是 2 。) 6 Reserved(保留字段): 总是 0 。 2 图像通道数量,包括透明通道,支持的范围从 1 到 56 。 4 像素表示的高度,支持的范围从 1 到 30,000 。 (PSB 最多 300,000.) 4 像素表示的宽度, 支持的范围从 1 到 30,000 。 (PSB* 最多 300,000) 2 Depth(深度): 每个通道使用的位数,支持的值包括 1, 8, 16, 32 。 2 文件的色彩模式。支持的值包括:Bitmap = 0; Grayscale = 1; Indexed = 2; RGB = 3; CMYK = 4; Multichannel = 7; Duotone = 8; Lab = 9




