一、引子
根据雅虎的网站优化准则,合并页面用到的图片,可以减少加载时发起的http请求数目,可以加速页面加载。具体能提速多少,本人没测试过,也就不好说了。
话说这排手上的项目里用到的图片都怎么合并,并不是不想合并,而是一个个图片去拼实在是太累了啊。另外桂总做的autosprite又还没成型,未能支持旧有项目;自己做的AutoSprites也是一个烂摊子——java写脚本功能的代码真不是一般的痛苦啊;至于炜哥的GoPng在线合图工具,说实话,这类需要人工定位图片的工具,我着实不喜欢。还是那句话,我可是个程序员呐,这种机械化无趣的工作就该交给电脑来完成,不然电脑是拿来干嘛的?
二、所谓智能
回到主题,智能合并CSS精灵图,何谓智能?对我来说是以下三点:
- 已有的项目基本不用修改即可使用该工具;
- 新项目还是能怎么爽怎么写,使用注释标注神马的一边去;
- 图片定位,css文件修改神马的都不用管。
嗯,综上所述,加上项目发布了,该优化性能了,今天花了一天把AutoSprites的烂摊子收拾了下,用NodeJs整了个iSpriter,有点出乎我的预料的快……是不是反证了java的开发效率慢?more
三、特色功能
目前基本实现了原有项目不用修改的需求,css写法、图片定位、css文件修改都不用care,写好配置文件之后就可以一劳永逸。
所支持的css写法如下:
- 普通的css写法:
div{
background: url(../images/tips_icons.png) ;
}
====>
div{
background: url(../images/sprite_1.png) -48px -48px;
}
- 文艺的css写法:
div{
background: url(../images/tips_icons.png) -42px 0;
}
====>
div{
background: url(../images/sprite_1.png) -48px -48px;
}
- 二逼的css写法:
div{
background-image: url(../images/tips_icons.png);
background-position: -42px 0;
}
====>
div{
background: url(../images/sprite_1.png) -48px -48px;
}
- 二逼est的css写法:
div{
background-image: url(../images/tips_icons.png);
background-color: #fff;
background-position: 0 -40px;
background-repeat: no-repeat;
background-origin: border-box;
background-clip: content-box;
}
====>
div{
background: #fff url(../images/sprite_1.png) -48px -48px no-repeat border-box content-box;
}
四、实现原理
该脚本使用了使用nodejs实现,依赖 CSSOM、node-canvas 两个模块,排列图片的算法选用了 bin-packing 算法,后续支持选择其他算法。具体实现代码就不展示了,请移步到github(https://github.com/iazrael/ispriter)查看。这里看下主要的执行步骤,代码写的比较块,方法名都是随便想了个差不多意思的,反正能运行就行了,对不对?哈哈^_^
var main = function(){
var //...
configFile = process.argv[2];
//1.读取配置文件
config = readConfig(configFile);
//2.读取所有css文件, readFiles方法已经把不合规定的文件过滤了
cssFileNameList = readFiles(config.cssRoot);
//...
for(var i = 0, fileName; fileName = cssFileNameList[i]; i++) {
cssContent = readFile(config.cssRoot + fileName);
//3.用cssom.parse把cssText转换成js对象
styleSheet = parseCssToStyleSheet(cssContent);
//4.把需要合并的图片url和cssRule收集起来
imageList = collectCSSRulesAndImages(styleSheet);
//...
//5.读取图片文件以及其大小
readImages(imageList);
//6.使用bin-packing算法计算图片的位置
positionResult = positionImages(imageList);
//...
//7.输出合并后的图片 sprite,并修改cssRule的background属性
drawImageAndPositionBackground(config.cssOutput, spriteName, canvasWidth, canvasHeight, imageList);
//...
//8.到最后了,输出修改后的css文件
writeFile(newFileName, styleSheet);
};
}
省略了些无关紧要的abc,主要步骤就是上面的8步了(天龙八部~~哈哈)。
五、使用方法
- 把源代码拷下来(需要先安装CSSOM和node-canvas):
git clone https://github.com/iazrael/ispriter.git
- 拷贝src下面的内容到项目目录,最好能改个名字如:spriter;
- 把spriter下的config.example.json拷贝出来,改成config.json,并修改里面的配置;
- 在项目目录运行:
node ./spriter/spriter.js config.json
- 嗯,可以执行后续的编译脚本了,就这么简单~
可能这里改的比较多一点的就是config.json了,其实里面也没几个参数:
{
"cssRoot": "./../test/css/",//需要合并图片的css所在目录
"format": "png",//要合并的图片格式,可以这样写:"png,jpg"
"algorithm": "growingpacker",//图片排列算法,目前只有这个一个...
"cssOutput": "./../test/output/css/",//css文件的输出目录
"imageOutput": "../images/",//图片的输出目录,相对于cssOutput
"outputPrefix": "sprite_",//合并后图片的名字前缀,其余字段用自增方式
"outputFormat": "png"//合并后的图片格式
}
如何?基本不用怎么改吧,啦啦啦~
六、不足与后续优化
总的来说,基本上没什么大问题,只是nodejs的环境在windows下比较难搞,主要就是node-canvas的安装。cssom是纯js实现的,因此可以放到项目目录;node-canvas就得想想办法了。或许搞个Web在线版的法子不错。
另外参数的配置可能还不够灵活,主要是自己用,没相应的需求难免有遗漏。还有参数名字也得斟酌下,免得歧义。
哦,漏了几点:
- 图片大小是自增的,不支持设置;
- 不支持使用了background-repeat:repeat-x or repeat-y的图片分类合并;
- 这个年代还有下载源代码来执行这么二逼的事,要想法子打个包一步就位;
这么一写,貌似还缺点还不少,呃,汗- -||。不过在够用的基础上,后面有时间在优化啦~~
欢迎踊跃提bug~~
---------------- 2012-6-17 update ------------------------
已经把所有依赖都打包到项目中,并已发到npm(http://search.npmjs.org/#/ispriter)
可以直接用下面的命令安装啦。
npm install ispriter
使用方法:
var spriter=require('ispriter');
spriter.merge(configFileName);
---------------- 2013-8-15 update ------------------------
感谢 node-pngjs 的作者,现在ispriter把node-canvas替换成了跟平台无关的node-pngjs,再也不用费劲心机的安装node-canvas啦!
鼓掌,哗啦啦O(∩_∩)O哈哈~
BTW:iSpriter的使用方法也更新了,请移步这里查看>>【更新】iSpriter – 智能合并CSS精灵图
哥表示偶的工具不用“人工定位”。。。。
@duwei 诶, 下午用发现了, 我错了...
感觉也不是很智能吧。还是要人工定位才行呀。要不然也是会重叠的。搞了几天以为多智能。白高兴一场。
@imking 不应该啊, 你的意思是自动排列出来的图片有重叠?
你好,我使用了你的这个工具,一直提示 Cannot find module '../build/default/canvas',我是在ubuntu下安装的。我把所需要的CSSOM和node-canvas模块都已经安装成功。请您帮忙解决一下,谢谢。我的QQ751109098,邮箱:guo-du@sohu.com.谢谢
@Spark 你好, 需要把node-canvas拷贝到ispriter/node_modules下面
mark!赞一个
E:
ode.js
ode_modules\ispriter
ode_modules\canvas>node 'E:\Program Files
ode<br>js
ode_modules
pm\bin
ode-gyp-bin\\..\..
ode_modules
ode-gyp\bin
ode-gyp.j<br>s' rebuild<br>gyp ERR! configure error<br>gyp ERR! stack Error: Command failed:<br>gyp ERR! stack at ChildProcess.exithandler (child_process.js:637:15)<br>gyp ERR! stack at ChildProcess.EventEmitter.emit (events.js:98:17)<br>gyp ERR! stack at maybeClose (child_process.js:735:16)<br>gyp ERR! stack at Process.ChildProcess._handle.onexit (child_process.js:802:<br>5)
虽然这个工具 跟我的需求不一致, 但是很感谢提到了bin-packing,最终还是解决了这个问题
@leeluolee ^_^, 很高兴能帮到你
@arb 你这是是不是node的配置不正确? windows要安装node-canvas是比较麻烦点, 还要安装vc2005等依赖
@arb 你现在可以使用不依赖node-canvas的ispriter了, npm update ispriter即可
工具不错,非常好用,但有个地方可以更加优化一下,可以添加一个压缩功能,就是说最后生成的css文件只有一个,image文件也只有一个
@cehui0303 嗯, 这个正在考虑中, 但是image只有一个的话会, 图片会变得比较大, 这里需要考虑下
@cehui0303 已经提供了提供将所有图片合并为一张, 同时所有css文件合并为一个文件的功能, 欢迎使用, ^_^
请问下,使用pngjs,如何进行图片的缩放,然后导出?
@SmallAiTT 你用 pngjs 把图片读入, 然后输出, 就已经会把没意义的 png 信息删除了