此博客不再维护,博客已迁移至 https://github.com/purplebamboo/blog/issues
文章目录
  1. 1. 思考方案
    1. 1.1. 基础支持
    2. 1.2. 生成html的方案
  2. 2. 结果
  3. 3. 约定
  4. 4. 结语

前段时间由于一些特殊原因,部门的外包都走了,于是大量活动页面压了过来。切完一个又来一个,实在对活动页面无爱,天天重复着切图的工作。想着如果能自动psd生成html就好了。

于是想着探讨下psd到html自动转换的可能性。

理想情况,一切自动化。一个psd,sou的一下转成代码优美,高兼容性的html页面。
事实证明这是不可能的。不然还要前端干啥。= =

不过如果是psd转成兼容性不错,代码一般或者略挫的页面,个人觉得还是有一定的可能性的。

这是建立在一定的调研基础上得出的结论。

  • psd.rb: 一个ruby的项目,可以将psd的所有图层信息都解析出来,包括left,top,以及字体图层的各种样式。还可以导出png图片。
  • ps-scripts: 其实photoshop cs6是可以用javascript写脚本来获取图层信息,导出图片等等功能的。具体的参见这里,有个专门的js库用来封装了这些api。可以到这个论坛里去看。
  • css hat 一个photoshop插件,可以用来复制图层的css样式。地址在这,还是收费的。
  • photoshop cc: 最新的photoshop cc版本已经可以查看复制单个图层的css了。参见这里

可见还是有很多人在尝试对psd的处理的。不过真正完美的psd到html的解决方案一直都没有。

据说世上本没有路,走的人多了便成了路。于是抱着玩玩的态度,我就尝试者,搞个命令行工具实现psd到html的转换过程。

作为一个前端,本来想用nodejs搞的,可psd.rb虽然也有nodejs的版本,但是还不完善。node还是太年轻了,幸好ruby我也略懂。

思考方案

基础支持

psd.rb这个库可以解析所有的图层信息。比如下面这个psd:
egpsd

解析完以后:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
{: children = > [{: type = > : group,
		: visible = > true,
		: opacity = > 1.0,
		: blending_mode = > "passthru",
		: name = > "blockgroup",
		: left = > 79,
		: right = > 229,
		: top = > 201,
		: bottom = > 223,
		: height = > 22,
		: width = > 150,
		: children = > [{: type = > : layer,
			: visible = > true,
			: opacity = > 1.0,
			: blending_mode = > "normal",
			: name = > "helloworld",
			: left = > 79,
			: right = > 229,
			: top = > 201,
			: bottom = > 223,
			: height = > 22,
			: width = > 150,
			: text = > {: value = > "helloworld",
				: font = > {: name = > "SimSun",
					: sizes = > [30.0],
					: colors = > [
						[232, 25, 25, 255]
					],
					: css = > "font-family: SimSun, AdobeInvisFont;\nfont-size: 30.0pt;\ncolor: rgba(232, 25, 25, 255);"
				},
				: left = > 0,
				: top = > 0,
				: right = > 0,
				: bottom = > 0,
				: transform = > {: xx = > 1.0,
					: xy = > 0.0,
					: yx = > 0.0,
					: yy = > 1.0,
					: tx = > 79.0,
					: ty = > 223.0
				}
			},
			: ref_x = > 0.0,
			: ref_y = > 0.0,
			: mask = > {},
			: image = > {: width = > 150,
				: height = > 22,
				: channels = > [{: id = > -1,
					: length = > 1735
				}, {: id = > 0,
					: length = > 134
				}, {: id = > 1,
					: length = > 134
				}, {: id = > 2,
					: length = > 134
				}]
			}
		}]
	}],
	: document = > {: width = > 300,
		: height = > 600
	...
}

看起来还是很简单的,解析出来后是一个hash。最外面是个根节点,他的子节点也就是children属性是type为group的节点,就是我们上面psd里面的组blockgroup(就是那个文件夹一样的),然后这个节点又有一个子节点type为layer。也就是我们的文字helloworld。

可以看到信息还是很全的,包括定位信息。样式信息都有。此外官方介绍可以直接对一个图层(layer)导出png图片。

生成html的方案

有了基础支持,就得想解决方案。虽然是一名前端,不过这个项目还是使用面向对象的思路实现比较好。我们应该把所有的层跟组看成一种区块,我们就叫做Convertor。所以我们定义一个基类叫Convertor,然后实现各种子类,比如针对组的block,针对图片的img,针对文字的text

我们通过psd.rb来遍历整个psd文档。组装成一个多叉树,说白了,就是从psd根节点开始挨个往子节点遍历,根据节点的类型实例化相应的convertor对象(img,text,block都是这种对象)。每个convertor对象都会保留父节点的对象的引用,以及自己的子节点对象们的引用。

每个convertor对象都自己实现css,和html的骨架。然后提供一个方法可以渲染出完整的代码。

每个对象渲染时都会递归的调用自己子节点的渲染代码。

所以我们最后只需要调用根节点的渲染方法,就可以得到所有的结果了。

所有的布局采用绝对定位实现。
对于block,会生成这样的html:

1
2
<div style="position:absolute"><div style="position:relative">...</div></div>
/*容器会对外绝对定位,对内相对定位。所有容器的子节点都相对父节点定位。*/

img生成img标记,计算出相对于外层block的left,top等值,来绝对定位。
text生成span标记,也是绝对定位。

这边给出Convertor类的大致类图:
leitu

  • psNode:psd.rb解析的节点对象,可以使用psd.rb提供的方法
  • index:当前图层的层级,就是z-index。会自动去计算。
  • childrenConvertors:子节点的引用
  • parentConvertor:父节点的引用
  • guid:用来获取唯一标示当前层的串
  • css_skeleton: 是个hash值,其实就是模板的data,用来渲染get_css_tpl返回的css模板。
  • html_skeleton:也是个hash值,用来渲染get_html_tpl返回的html模板。

大部分的子节点只需要重写css_skeleton与html_skeleton。来达到生成自己特定的代码的目的。

整体的代码结构如下:

code

结果

最后我就实现了这样一个命令行工具了psd2html。
github地址:https://github.com/cherishpeace/psd2html

可以直接使用gem安装

1
gem install psd2html

然后就可以直接命令行调用了。

1
psd2html /vagrant/project/psd2html/example/simple.psd ./test.html

约定

我们看下simple.psd:
simple1

注意到每个内容后面我们都使用了|标记

比如促销价后面跟的是text,它将告诉我们的解析器把它当成一个文本处理这样解析引擎就会生成一个字体的html容器。而按钮图后面跟的是img,代表将当前图层当做图片处理,这样引擎就会生成一个img的html代码。
block则代表一个区块,也就是一个容器,用来包裹里面的内容。

注意到block前面的container了没。container将作为生成的html容器的class名称。
所以完整的命名为 标题|class名称|类型约定

具体的逻辑是这样的:

  1. 通过|来分割标记,如果有类型约定,也有class名称,就会正常赋值
  2. 如果只有一个类型约定标记,就会使用标题作为class,标题为英文最好,如果是中文的话,会使用MD5生成一个串作为class。
  3. 什么都没有的话,程序会自动解析,加上默认的类型约定

类型约定是整个工具的灵魂。它将告诉引擎怎么解析图层。目前总共有下面这些类型约定:

  • block group的默认,根据图层信息,生成一个容器。容器会对外绝对定位,对内相对定位。所有容器的子节点都相对父节点定位。
  • block-link block的变种,不同的是外面不再是div,而是变成了可以点击的a标记。
  • text 文字的默认处理,根据图层信息,生成一个span容器,里面是文字内容。
  • text-link text的变种。外面会是a标记,这样变得可点击。
  • img 图片的默认处理,根据图层信息,生成一个img标签。
  • img-link img的变种,会在img外面包一层a标记,变的可点击。
  • img-bg img的变种,会将当前图片作为父节点的背景图像。

对于默认的psd,程序会自动分析出当前图层是group就用block,是text就用text,还有img就用img。

所以simple2.psd这样的psd,是可以正常解析的。
simple2.psd如下:
simple2

结语

虽然这个东西最后还是有一定的局限性。不过理论上可以无限扩展的,再制作不同的约定,实现处理类,是可以实现很多东西的。

文章目录
  1. 1. 思考方案
    1. 1.1. 基础支持
    2. 1.2. 生成html的方案
  2. 2. 结果
  3. 3. 约定
  4. 4. 结语