UNPKG

grunt-hellosprite

Version:

A grunt plugin to help front engineer creating css sprite.

230 lines (188 loc) 10.1 kB
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(); }); }); };