grunt-hellosprite
Version:
A grunt plugin to help front engineer creating css sprite.
230 lines (188 loc) • 10.1 kB
JavaScript
var spritesmith = require('spritesmith'),
fs = require('fs'),
path = require('path'),
gm = require('gm');
module.exports = function (grunt) {
"use strict";
// Export the SpriteMaker function
grunt.registerMultiTask('hellosprite', 'Create sprites and updates css.', function () {
var that = this,
done = this.async(),
log = console.log;
var options = this.options({
'imagestamp':true,
'cssstamp':true
});
var _spriteSmithWrapper = function (file, callback) {
var src = file.src[0],
destDir = file.orig.dest,
dest = file.dest,
regex = new RegExp('\\/?([^.\\/]*)\\..*', 'ig'),
fileNameList = src.match(regex),
fileName = fileNameList[0].replace(regex, '$1'),
destCSS = destDir +"css/" + fileName + file.orig.ext,
destSPRITE = destDir +"sprite/" + fileName + ".png";
var sliceList = [], //
sliceCodeList = [], //background-image:url("../slice/xxx.png")
sliceImgUrlList = [], //slice/xxx.png
sliceCode = [], //
data = []; //
// 获取slice部分的代码
var getSliceList = function () {
var regex = new RegExp('background-image:[\\s]*url\\(["\']?(?!http[s]?|/)[^;]*?(slice[\\w\\d\\s!./\\-\\_@]*\\.[\\w?#]+)["\']?\\)[^;}]*;?', 'ig'),
pathToResource;
// 获取整个样式
data = grunt.file.read(src);
// 匹配到所有的background-image:url("../slice/xxx.png")
sliceCodeList = data.match(regex);
// 对background-image进行遍历,取出其中的slice/xxx.png
if (sliceCodeList !== null) {
for (var x = 0; x < sliceCodeList.length; x++) {
// 匹配到css里图片的路径信息 slice/xxx.png
pathToResource = sliceCodeList[x].replace(regex, '$1');
sliceImgUrlList[x] = pathToResource;
sliceList[pathToResource] = sliceCodeList[x];
}
}
else callback(false);
};
// 生成雪碧图
var createSprite = function () {
var config = that.data.options;
delete config.sprite;
config.src = sliceImgUrlList;
if (sliceImgUrlList.length > 0) {
// spritesmiteh 库调用
spritesmith(config, function (err, result) {
if (err) {
grunt.fatal(err);
return callback(err);
}
else {
// 生成sprite
grunt.file.write(destSPRITE, result.image, { encoding: 'binary' });
grunt.log.writelns(("Er, " + destSPRITE + " has been created !"));
var tmpResult = result.coordinates;
for (var key in tmpResult) {
sliceCode[ key ] = tmpResult[ key ];
sliceCode[ key ].sprite = sliceList[ key ];
}
replaceCode();
}
});
}
else {
grunt.file.copy(src, destCSS);
grunt.log.writelns((destCSS + " has been copied !"));
}
};
// 替换css里的background-image代码部分
var replaceCode = function () {
var img;
var datasprite = grunt.file.read(src);
for (var key in sliceCode) {
img = sliceCode[ key ];
var code ='background-image: url("../sprite/' + fileName + '.png';
if(options.imagestamp) code+="?"+grunt.template.today("yyyymmddHHmmss");
code+= '");';
code+=' background-position: -' + img.x + 'px -' + img.y + 'px;';
datasprite = datasprite.replace(img.sprite,code);
}
// 生成新的样式
grunt.file.write(destCSS, datasprite);
grunt.log.writelns((destCSS + " has been created !"));
retinaSprite();
};
// 生成retina雪碧图
var retinaSprite = function () {
var regex = new RegExp('.*(slice\\/[\\w\\d\\s!./\\-\\_@]*)\\.([\\w?#]+)["\']?\\)[^;}]*;?', 'ig');
// var regex = new RegExp('.*slice\\/(.*)\\.(\\w*)', 'ig');
RegExp.escape = function (s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
};
var retinaSliceList = [];
var retinaImgUrlList = [];
var retinaSpriteSize = new Object();
for (var i = 0; i < sliceCodeList.length; i++) {
var key = sliceCodeList[i];
var retinaIconUrl = key.replace(regex, '$1') + "@2x." + key.replace(regex, '$2');
if (grunt.file.exists(retinaIconUrl)) {
var regexClassNameString = '(\\.?[^}]*?)\\s?{\\s?[^}]*?' + RegExp.escape(key);
var regexClassName = new RegExp(regexClassNameString, 'ig');
var classNameResult = data.match(regexClassName);
var className = classNameResult[0].replace(regexClassName, '$1');
var retinaItem = new Object();
retinaImgUrlList.push(retinaIconUrl);
retinaItem.className = className;
retinaSliceList[retinaIconUrl] = retinaItem;
}
}
if (retinaImgUrlList.length > 0) {
var config = that.data.options;
var spritedest = destDir +"sprite/" + fileName + "@2x.png";
config.src = retinaImgUrlList;
// spritesmiteh 库调用
spritesmith(config, function (err, result) {
if (err) {
grunt.fatal(err);
return callback(err);
}
else {
// 生成sprite
grunt.file.write(spritedest, result.image, { encoding: 'binary' });
gm(spritedest).size(function (err, size) {
if (!err) {
retinaSpriteSize = size;
if (retinaSpriteSize.width % 2 == 1 || retinaSpriteSize.height % 2 == 1) {
grunt.fail.fatal("警告:所有的雪碧图icon尺寸必须是偶数的!请检查!");
}
grunt.log.writelns(("Er, " + spritedest + " has been created !"));
var tmpResult = result.coordinates;
// console.log(tmpResult);
for (var key in tmpResult) {
retinaSliceList[ key ].sprite = tmpResult[ key ];
}
createRetinaCode(retinaSliceList, retinaSpriteSize);
addCssStamp();
callback(false);
}
});
}
});
}
else callback(false);
};
var addCssStamp = function(){
var sourcedataurl = destCSS;
var sourcedata = grunt.file.read(sourcedataurl);
if(options.cssstamp) sourcedata+='.TmTStamp{content:"'+ grunt.template.today("yyyymmddHHmmss") +'"}';
grunt.file.write(destCSS, sourcedata);
};
var createRetinaCode = function (retinaSliceCode, retinaSpriteSize) {
var sourcedataurl = destCSS;
var sourcedata = grunt.file.read(sourcedataurl);
var retinaCssCode = '';
var img;
retinaCssCode += '@media only screen and (-webkit-min-device-pixel-ratio: 1.5),only screen and (min--moz-device-pixel-ratio: 1.5),only screen and (min-resolution: 240dpi) {';
for (var key in retinaSliceCode) {
img = retinaSliceCode[key].sprite;
retinaCssCode += retinaSliceCode[key].className;
retinaCssCode += '{background-image:url("../sprite/' + fileName + '@2x.png';
if(options.imagestamp) retinaCssCode+="?"+grunt.template.today("yyyymmddHHmmss");
retinaCssCode +='");';
retinaCssCode += 'background-position: -' + (img.x) / 2 + 'px -' + (img.y) / 2 + 'px;background-size:' + (retinaSpriteSize.width) / 2 + 'px;}';
}
retinaCssCode += '}';
sourcedata += retinaCssCode;
grunt.file.write(destCSS, sourcedata);
grunt.log.writelns((destCSS + " retina version has been created !"));
};
getSliceList();
createSprite();
};
// Process starter
grunt.util.async.forEachSeries(this.files, _spriteSmithWrapper, function (err) {
done();
});
});
};