先看几张PSD海报

《中秋节祝福海报》

image.png

PSD转H5演示视频

ly 2019-08-08 13.00.15.mp4 (761.36KB)

需求场景

  1. 中秋节将近,某点评App,需要在其App内部做一个活动页面,向用户宣传一下中秋节的一些活动
  2. 教师节将近,某校园App,需要在其App上做一个活动页面,大致的意思是 希望表达对老师的崇敬之情
  3. 最近一段时间,房地产市场火爆,某房产中介正在招兵买马,希望在其App上或者朋友圈发一个招聘H5

以前的模式

  1. 设计师同学用 Photoshop 做好了类似下图的一张 PSD,然后再使用SKetch 进行标注。准备完毕之后,将相关的文件发给前端同学。
  2. 前端同学拿到 Sketch 和 PSD 之后,开始按照标注进行制作H5页面,经过一到两天的静态页面开发(具体情况具体分析,具体的开发周期取决于页面的复杂度),期间也和设计师、产品经理同学进行了协商,最终还原了设计稿,双方都觉得比较满意,觉得可以上线了。
  3. 前端同学 把图片 + 代码 上传到服务器,配置一个域名之后,正式上线。大家就可以通过 h5.xxx.com 访问到一个中秋节H5页面了

现在 在鲁班H5中,这么做就可以了:

  1. 设计师同学只要打开鲁班的编辑器
  2. 点击图中的 Ps (导入PSD)
  3. 在弹出的页面中,上传需要制作成H5的PSD 文件即可
  4. 大部分情况下,不到一分钟,一个和 PSD 长得几乎一样的 H5 页面就会出现在中间画布上了(具体时间取决于psd 文件大小和图层多少)
  5. 点击预览,设计师再做一些细节的调整,点击发布,会得到一个 该H5作品对应的网址,制作完成!
  6. 我们发现,上述流程比之前工程师写代码的流程差不多节约了 三到四天的时间

    image.png

实现原理

基础认知

  1. 我们的目的是:PSD -> H5
  2. 根据我们之前的讲解,以《中秋节祝福海报》为例,我们知道一个简单的H5本质上对应的是一个 JSON,如下所述(其中:lbp意思是 lu-ban-plugin):

    1. [
    2. {
    3. name: '兔子图片',
    4. type: 'lbp-image'
    5. commonStyle: {
    6. top: '100px',
    7. left: '120px'
    8. }
    9. },
    10. {
    11. name: '英文祝福文字',
    12. type: 'lbp-text'
    13. text: 'Dear Friends',
    14. commonStyle: {
    15. top: '240px',
    16. left: '160px'
    17. }
    18. }
    19. ]
    1. 那么 PSD -> H5 也就意味着,我们需要把 PSD 文件转换为上面的JSON结构,我们大概看下:
    2. 假如我们有某种办法,能够得到PSD中的图层结构,能够对 PSD 文件做以下操作:
      1. 把每个图层中的图片保存下来
      2. 获得该图层的 左上角 相对于 整个PSD 的相对左上角 的位置(以左上角为坐标原点,向右为X正轴,向下为Y正轴),也就是:{left, top}
      3. 获得文字图层中的文字内容、文字大小、颜色、字体等信息
      4. 再高级点:图层的渐变色等
    3. 如果上面的办法可行,我们可以把 PSD 文件也抽象成一个 JSON(如下)
  1. [
  2. {
  3. name: '兔子图片图层',
  4. type: 'image',
  5. position: {
  6. top: '100px',
  7. left: '120px'
  8. },
  9. },
  10. {
  11. name: '左灯笼图片图层',
  12. type: 'image',
  13. position: {
  14. top: '100px',
  15. left: '120px'
  16. },
  17. },
  18. {
  19. name: '右灯笼图片图层',
  20. type: 'image',
  21. position: {
  22. top: '100px',
  23. left: '120px'
  24. },
  25. },
  26. {
  27. name: '英文祝福文字图层',
  28. type: 'text',
  29. text: 'Dear Friend',
  30. position: {
  31. top: '240px',
  32. left: '160px'
  33. },
  34. }
  35. ]

殊途同归

可以发现,PSD 文件的数据结构和鲁班中的一个作品的数据结构是非常相似的
我们来整理一下二者之间相似之处(如下),是不是发现PSD 的结构和鲁班在很多地方是可以一一对应的上的呢?

  1. image(psd) <-> lbp-picture(luban)
  2. text (psd) <-> lbp-text(luban)

实现路径

  1. 那么让我们看一下我们之前的假设: 假设我们可以分析PSD的结构,并从中得到图片、文字等图层信息
  2. 现在可以告诉大家了,上面的假设,我们完全可以实现。
  3. 首先,让我们重新认识一下PSD这个文件格式,通常情况下,我们都知道它是Photoshop导出的文件格式。其实它也是一个有着自己独特风格的文件格式。
  4. 我们先来看一份东西: PSD 的文件规范,访问地址:https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/

看一下 PSD文件结构,有一种被计算机网络七层结构支配的恐惧😂

3EB608DB-16F4-4D9E-9E4A-C0331C1AD808.jpeg 6EF87D96-B146-43EC-A6AC-475F03F18305.jpeg

  1. 也就是说 PSD 文件是由几个部分组成的:

    1. File Header
    2. Color
    3. Image Resources
    4. Layer And Mask Information
    5. Image Data
  2. 我们借助 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());

  1. ```javascript
  2. root {
  3. psd: PSD {
  4. file: File {
  5. data:<Buffer 38 4....> ,
  6. pos: 7577696
  7. },
  8. parsed: true,
  9. header: Header {
  10. file: [File],
  11. sig: '8BPS',
  12. version: 1,
  13. channels: 4,
  14. rows: 900,
  15. cols: 600,
  16. depth: 8,
  17. mode: 3
  18. },
  19. resources: LazyExecute {
  20. obj: [Resources],
  21. file: [File],
  22. startPos: 30,
  23. loaded: false,
  24. loadMethod: 'parse',
  25. loadArgs: [],
  26. passthru: []
  27. },
  28. layerMask: LazyExecute {
  29. obj: [LayerMask],
  30. file: [File],
  31. startPos: 28884,
  32. loaded: true,
  33. loadMethod: 'parse',
  34. loadArgs: [],
  35. passthru: []
  36. },
  37. image: LazyExecute {
  38. obj: [Image],
  39. file: [File],
  40. startPos: 7577696,
  41. loaded: false,
  42. loadMethod: 'parse',
  43. loadArgs: [],
  44. passthru: []
  45. }
  46. },
  47. layer: {
  48. name: null,
  49. left: 0,
  50. right: 600,
  51. top: 0,
  52. bottom: 900,
  53. height: null,
  54. width: null,
  55. node: [Circular]
  56. },
  57. parent: null,
  58. _children: [Group {
  59. layer: [Layer],
  60. parent: [Circular],
  61. _children: [Array],
  62. name: '站长素材(sc.chinaz.com)',
  63. forceVisible: null,
  64. coords: [Object],
  65. topOffset: 0,
  66. leftOffset: 0
  67. }
  68. ],
  69. name: null,
  70. forceVisible: null,
  71. coords: {
  72. top: 0,
  73. bottom: 900,
  74. left: 0,
  75. right: 600
  76. },
  77. topOffset: 0,
  78. leftOffset: 0
  79. }

我们来对上面的数据结构进行一些简单的分析,重点看一下其中的 root.psd ,也就是下面的数据结构。
其中 file:File 类似 TypeScript 的写法,表示 file 变脸的类型是 File。LazyExecute 类似于迭代器的感觉,用的时候,执行一下才能得到结果,也就是 realResource = root.psd.resource() 类似这种感觉

  1. {
  2. file: File {
  3. data:<Buffer 38... >,
  4. pos: 7577696
  5. },
  6. parsed: true,
  7. header: Header {
  8. file: [File],
  9. sig: '8BPS',
  10. version: 1,
  11. channels: 4,
  12. rows: 900,
  13. cols: 600,
  14. depth: 8,
  15. mode: 3
  16. },
  17. resources: LazyExecute {
  18. obj: [Resources],
  19. file: [File],
  20. startPos: 30,
  21. loaded: false,
  22. loadMethod: 'parse',
  23. loadArgs: [],
  24. passthru: []
  25. },
  26. layerMask: LazyExecute {
  27. obj: [LayerMask],
  28. file: [File],
  29. startPos: 28884,
  30. loaded: true,
  31. loadMethod: 'parse',
  32. loadArgs: [],
  33. passthru: []
  34. },
  35. image: LazyExecute {
  36. obj: [Image],
  37. file: [File],
  38. startPos: 7577696,
  39. loaded: false,
  40. loadMethod: 'parse',
  41. loadArgs: [],
  42. passthru: []
  43. }
  44. }

我们对其进行进一步的整理,发现它其实可以整理为下面的这种结构

  1. root.psd: {
  2. file<File>: {},
  3. parsed: true,
  4. header<Header> : {},
  5. resources<LazyExecute>: {},
  6. layerMask<LazyExecute>: {},
  7. image<LazyExecute>: {}
  8. }

image.png

我们仔细看一下,上面的数据结构 是不是和 PSD 文件规范 里面描述的 Photoshop file Structure 能够一一匹配上了。
到这里相信大家应该能够明白了吧,我们通过程序(主要借助 psd.js)发现可以对 PSD 文件进行解读,从中得到我们想要的数据。接下来,我们对 Photoshop file Structure进行略微深入一些的解读

Photoshop file Structure 解读

  1. 在这里,我就不班门弄斧了,大家感兴趣的话,可以看看这位小哥iamgqb 翻译的PSD 文件格式规范/Adobe Photoshop File Formats Specification. 再次表示感谢
  2. 我会补充一些说明,也就是对规范中的一些字段在实际有何用途

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

2. Color

3. Image Resources

4. Layer And Mask Information

5. Image Data