一、引子

根据雅虎的网站优化准则,合并页面用到的图片,可以减少加载时发起的http请求数目,可以加速页面加载。具体能提速多少,本人没测试过,也就不好说了。

话说这排手上的项目里用到的图片都怎么合并,并不是不想合并,而是一个个图片去拼实在是太累了啊。另外桂总做的autosprite又还没成型,未能支持旧有项目;自己做的AutoSprites也是一个烂摊子——java写脚本功能的代码真不是一般的痛苦啊;至于炜哥的GoPng在线合图工具,说实话,这类需要人工定位图片的工具,我着实不喜欢。还是那句话,我可是个程序员呐,这种机械化无趣的工作就该交给电脑来完成,不然电脑是拿来干嘛的?

二、所谓智能

回到主题,智能合并CSS精灵图,何谓智能?对我来说是以下三点:

  1. 已有的项目基本不用修改即可使用该工具;
  2. 新项目还是能怎么爽怎么写,使用注释标注神马的一边去;
  3. 图片定位,css文件修改神马的都不用管。

嗯,综上所述,加上项目发布了,该优化性能了,今天花了一天把AutoSprites的烂摊子收拾了下,用NodeJs整了个iSpriter,有点出乎我的预料的快……是不是反证了java的开发效率慢?more

三、特色功能

目前基本实现了原有项目不用修改的需求,css写法、图片定位、css文件修改都不用care,写好配置文件之后就可以一劳永逸。

所支持的css写法如下:

  1. 普通的css写法:
div{
    background: url(../images/tips_icons.png) ;
}
====>
div{
    background: url(../images/sprite_1.png) -48px -48px;
}
  1. 文艺的css写法:
div{
    background: url(../images/tips_icons.png) -42px 0;
}
====>
div{
   background: url(../images/sprite_1.png) -48px -48px;
}
  1. 二逼的css写法:
div{
   background-image: url(../images/tips_icons.png);
   background-position: -42px 0;
}
====>
div{
   background: url(../images/sprite_1.png) -48px -48px;
}
  1. 二逼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实现,依赖 CSSOMnode-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步了(天龙八部~~哈哈)。

五、使用方法

  1. 把源代码拷下来(需要先安装CSSOMnode-canvas):
git clone https://github.com/iazrael/ispriter.git
  1. 拷贝src下面的内容到项目目录,最好能改个名字如:spriter;
  2. 把spriter下的config.example.json拷贝出来,改成config.json,并修改里面的配置;
  3. 在项目目录运行:
    node ./spriter/spriter.js config.json
  1. 嗯,可以执行后续的编译脚本了,就这么简单~

可能这里改的比较多一点的就是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在线版的法子不错。

另外参数的配置可能还不够灵活,主要是自己用,没相应的需求难免有遗漏。还有参数名字也得斟酌下,免得歧义。

哦,漏了几点:

  1. 图片大小是自增的,不支持设置;
  2. 不支持使用了background-repeat:repeat-x or repeat-y的图片分类合并;
  3. 这个年代还有下载源代码来执行这么二逼的事,要想法子打个包一步就位;

这么一写,貌似还缺点还不少,呃,汗- -||。不过在够用的基础上,后面有时间在优化啦~~

欢迎踊跃提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精灵图

Comments
Write a Comment
  • duwei reply

    哥表示偶的工具不用“人工定位”。。。。

  • Azrael reply

    @duwei 诶, 下午用发现了, 我错了...

  • imking reply

    感觉也不是很智能吧。还是要人工定位才行呀。要不然也是会重叠的。搞了几天以为多智能。白高兴一场。

  • iAzrael reply

    @imking 不应该啊, 你的意思是自动排列出来的图片有重叠?

  • Spark reply

    你好,我使用了你的这个工具,一直提示 Cannot find module '../build/default/canvas',我是在ubuntu下安装的。我把所需要的CSSOM和node-canvas模块都已经安装成功。请您帮忙解决一下,谢谢。我的QQ751109098,邮箱:guo-du@sohu.com.谢谢

  • iAzrael reply

    @Spark 你好, 需要把node-canvas拷贝到ispriter/node_modules下面

  • levin reply

    mark!赞一个

  • arb reply

    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)

  • leeluolee reply

    虽然这个工具 跟我的需求不一致, 但是很感谢提到了bin-packing,最终还是解决了这个问题

  • iAzrael reply

    @leeluolee ^_^, 很高兴能帮到你

  • iAzrael reply

    @arb 你这是是不是node的配置不正确? windows要安装node-canvas是比较麻烦点, 还要安装vc2005等依赖

  • iAzrael reply

    @arb 你现在可以使用不依赖node-canvas的ispriter了, npm update ispriter即可

  • cehui0303 reply

    工具不错,非常好用,但有个地方可以更加优化一下,可以添加一个压缩功能,就是说最后生成的css文件只有一个,image文件也只有一个

  • iAzrael reply

    @cehui0303 嗯, 这个正在考虑中, 但是image只有一个的话会, 图片会变得比较大, 这里需要考虑下

  • iAzrael reply

    @cehui0303 已经提供了提供将所有图片合并为一张, 同时所有css文件合并为一个文件的功能, 欢迎使用, ^_^

  • SmallAiTT reply

    请问下,使用pngjs,如何进行图片的缩放,然后导出?

  • iAzrael reply

    @SmallAiTT 你用 pngjs 把图片读入, 然后输出, 就已经会把没意义的 png 信息删除了

Tags

css3   魅力CSS   nodejs   loading   CSS   疯狂的菊花   html5   animation   compiler   编译脚本   png   WordPress   智能   旅行   优化   模板   历史记录   跨域   manifest   frame   canvas   动画   js   离线应用   codelet   transform   抽取   java   兼容问题   发布脚本   富文本   那一年在他乡   htaccess   iframe   帧动画   加载速度   intelligent   跨浏览器   DNS解析   插件   checkbox   单边   step-start   vary   复选框   自动更新   转换   文本溢出   盒子阴影   menu   blob   西安   滑动背景   box-shadow   内存占用   键盘事件   python   auto   text overflow   background   所见所得   android   rotate   字节数   合并   文本框   slide   字符串连接符   协议   伪类   兄弟选择符   网格   节点位置比较   空白   斜线拼接   自定义命令   溢出   clock   素描   无法更新   分隔符   字符编码   body   下载文件   精灵图   step   nodej   ubuntu   apache   css3选择器   创建文件   多级菜单   编辑状态   ajax   阴影   垂直   chrome   管道   时钟   firefox   背景   文件上传   createobjecturl   游记   下载   放射渐变   版本号   宽高   照片   localStorage   渐变背景   图片   图片拼接   属性值检测   自动生成   计算   返回键   oauth   合图   reset   调用   cavnas   漏洞   按钮   margin   线性渐变   xsrf   被黑   tab   checked   修复   border   消失   step-end   sprite   common-upload   菜单   兄弟选择器   字符串   svn   九寨沟   缩进   css遮罩   svg   添加系统服务   gzip   插入代码   动态   加速   模拟