From d3d365a74ac6cf8395add8e934e214726e9e3591 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 11 Sep 2015 19:59:06 -0500 Subject: [PATCH] Move sprites task to gulp --- Gruntfile.js | 73 +----------------------- package.json | 9 +-- tasks/gulp-sprites.js | 128 ++++++++++++++++++++++++++++++++++++++++++ vagrant.sh | 4 +- 4 files changed, 136 insertions(+), 78 deletions(-) create mode 100644 tasks/gulp-sprites.js diff --git a/Gruntfile.js b/Gruntfile.js index 6eef06ec03..1b72bb7dc6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -2,57 +2,6 @@ var _ = require('lodash'); module.exports = function(grunt) { - // Ported from shared - // So this sucks. Mobile Safari can't render image files > 1024x1024*3, so we have to break it down to multiple - // files in this hack approach. See https://github.com/Ensighten/grunt-spritesmith/issues/67#issuecomment-34786248 - var images = grunt.file.expand('common/img/sprites/spritesmith/**/*.png'); -// var totalDims = {width:0,height:0}; -// _.each(images, function(img){ -// var dims = sizeOf(img); -// if(!dims.width || !dims.height) console.log(dims); -// totalDims.width += dims.width; -// totalDims.height += dims.height; -// }) - var COUNT = 7;//Math.ceil( (totalDims.width * totalDims.height) / (1024*1024*3) ); - //console.log({totalDims:totalDims,COUNT:COUNT}); - - var sprite = {}; - _.times(COUNT, function(i){ - var sliced = images.slice(i * (images.length/COUNT), (i+1) * images.length/COUNT) - sprite[''+i] = { - src: sliced, - dest: 'common/dist/sprites/spritesmith'+i+'.png', - destCss: 'common/dist/sprites/spritesmith'+i+'.css', - engine: 'phantomjssmith', - algorithm: 'binary-tree', - padding:1, - cssTemplate: 'common/css/css.template.mustache', - cssVarMap: function (sprite) { - // For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class, which works as a - // 60x60 image pointing at the proper part of the 90x90 sprite. - // We set up the custom info here, and the template makes use of it. - if (sprite.name.match(/hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears/) || sprite.name=='head_0') { - sprite.custom = { - px: { - offset_x: "-" + (sprite.x + 25) + "px", - offset_y: "-" + (sprite.y + 15) + "px", - width: "" + 60 + "px", - height: "" + 60 + "px" - } - } - } - if (~sprite.name.indexOf('shirt')) - sprite.custom.px.offset_y = "-" + (sprite.y + 30) + "px"; // even more for shirts - } - /*,cssOpts: { - cssClass: function (item) { - return '.' + item.name; //'.sprite-' + item.name; - } - }*/ - } - }); - - // Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), @@ -69,24 +18,7 @@ module.exports = function(grunt) { }, clean: { - build: ['website/build'], - sprite: ['common/dist/sprites'] - }, - - sprite: sprite, - - imagemin: { - spritesmith: { - options: { - optimizationLevel: 7 - }, - files: [{ - expand: true, - flatten: true, - src: ["common/dist/sprites/*.png"], - dest: "common/dist/sprites/" - }] - } + build: ['website/build'] }, cssmin: { @@ -200,7 +132,6 @@ module.exports = function(grunt) { }); // Register tasks. - grunt.registerTask('compile:sprites', ['clean:sprite', 'sprite', 'imagemin', 'cssmin']); grunt.registerTask('build:prod', ['loadManifestFiles', 'clean:build', 'browserify', 'uglify', 'stylus', 'cssmin', 'copy:build', 'hashres']); grunt.registerTask('build:dev', ['browserify', 'stylus']); grunt.registerTask('build:test', ['test:prepare:translations', 'build:dev']); @@ -222,8 +153,6 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-spritesmith'); - grunt.loadNpmTasks('grunt-contrib-imagemin'); grunt.loadNpmTasks('grunt-hashres'); grunt.loadNpmTasks('grunt-karma'); diff --git a/package.json b/package.json index 8c1203bcf4..2cb364291a 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "grunt-contrib-watch": "~0.6.1", "grunt-hashres": "~0.4.1", "grunt-karma": "~0.6.2", - "grunt-spritesmith": "~3.5.0", "gulp": "^3.9.0", "gulp-grunt": "^0.5.2", "icalendar": "lefnire/node-icalendar#e06da0e55901f0ba940dfadc42c158ed0b1fead9", @@ -82,6 +81,7 @@ "scripts": { "test": "gulp test", "start": "gulp run:dev", + "sprites": "gulp sprites:compile", "postinstall": "bower --config.interactive=false install -f; gulp build;", "coverage": "COVERAGE=true mocha --require register-handlers.js --reporter html-cov > coverage.html; open coverage.html" }, @@ -94,8 +94,10 @@ "event-stream": "^3.2.2", "expect.js": "~0.2.0", "glob": "^4.3.5", - "grunt-contrib-imagemin": "^0.9.4", + "gulp-clean": "^0.3.1", + "gulp-imagemin": "^2.3.0", "gulp-nodemon": "^2.0.4", + "gulp.spritesmith": "^4.1.0", "istanbul": "^0.3.14", "karma": "~0.10.2", "karma-chai-plugins": "~0.1.0", @@ -112,12 +114,11 @@ "karma-requirejs": "~0.2.0", "karma-script-launcher": "~0.1.0", "lcov-result-merger": "^1.0.2", + "merge-stream": "^1.0.0", "mocha": "~1.12.1", "mongoskin": "~0.6.1", - "phantomjssmith": "~0.5.4", "protractor": "~2.0.0", "rewire": "^2.3.3", - "rimraf": "^2.2.8", "shelljs": "^0.4.0", "sinon": "1.15.4", "sinon-chai": "^2.7.0", diff --git a/tasks/gulp-sprites.js b/tasks/gulp-sprites.js new file mode 100644 index 0000000000..25e7c28ddb --- /dev/null +++ b/tasks/gulp-sprites.js @@ -0,0 +1,128 @@ +import gulp from 'gulp'; +import imagemin from 'gulp-imagemin'; +import spritesmith from 'gulp.spritesmith'; +import clean from 'gulp-clean'; +import sizeOf from 'image-size'; +import merge from 'merge-stream'; +import {sync} from 'glob'; +import {times, each} from 'lodash'; + +const SPRITES_SRC = sync('common/img/sprites/spritesmith/**/*.png'); +const DIST_PATH = 'common/dist/sprites/'; + +// https://github.com/Ensighten/grunt-spritesmith/issues/67#issuecomment-34786248 +const MAX_SPRITESHEET_SIZE = 1024 * 1024 * 3; +const SPRITESHEET_COUNT = _calculateNumberOfSpritesheets(); +const NUMBER_OF_SPRITES_PER_SHEET = SPRITES_SRC.length / SPRITESHEET_COUNT; + +let spritesTasks = ['sprites:clean']; + +times(SPRITESHEET_COUNT, (i) => { + let slicedSrc = _getSliceSrc(i); + + let taskName = `sprites:${i}`; + spritesTasks.push(taskName); + + gulp.task(taskName, () => { + let spriteData = gulp.src(slicedSrc) + .pipe(spritesmith({ + imgName: `spritesmith${i}.png`, + cssName: `spritesmith${i}.css`, + algorithm: 'binary-tree', + padding:1, + cssTemplate: 'common/css/css.template.mustache', + cssVarMap: _cssVarMap + })); + + let imgStream = spriteData.img + .pipe(imagemin()) + .pipe(gulp.dest(DIST_PATH)); + + let cssStream = spriteData.css + .pipe(gulp.dest(DIST_PATH)); + + return merge(imgStream, cssStream); + }); +}); + +gulp.task('sprites:clean', (done) => { + gulp.src(`${DIST_PATH}spritesmith*`) + .pipe(clean()); + + done(); +}); + +gulp.task('sprites:checkCompiledDimensions', () => { + console.log('Verifiying that images do not exceed max dimensions'); + + let numberOfSheetsThatAreTooBig = 0; + + times(SPRITESHEET_COUNT, (i) => { + let fileName = `spritesmith${i}.png`; + let spritesheetPath = `${DIST_PATH}${fileName}` + let spriteSize = _calculateImgDimensions(spritesheetPath); + + + if (spriteSize > MAX_SPRITESHEET_SIZE) { + numberOfSheetsThatAreTooBig++; + console.error(`WARNING: ${fileName} is too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`); + } + }); + + if (numberOfSheetsThatAreTooBig > 0) { + console.error(`${numberOfSheetsThatAreTooBig} sheets are too big :(`); + console.error('Mobile Safari may be unhappy with you'); + } +}); + +gulp.task('sprites:compile', spritesTasks, () => { + gulp.run('sprites:checkCompiledDimensions'); +}); + +function _getSliceSrc(num) { + let start = num * NUMBER_OF_SPRITES_PER_SHEET; + let end = (num + 1) * NUMBER_OF_SPRITES_PER_SHEET; + let src = SPRITES_SRC.slice(start, end) + + return src; +} + +function _calculateNumberOfSpritesheets() { + let totalPixels = 0; + + each(SPRITES_SRC, function(img){ + totalPixels += _calculateImgDimensions(img); + }); + + let numberOfSpriteSheets = Math.ceil(totalPixels / MAX_SPRITESHEET_SIZE); + + return numberOfSpriteSheets; +} + +function _calculateImgDimensions(img) { + let dims = sizeOf(img); + + if(!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims); + + let totalPixelSize = dims.width * dims.height; + + return totalPixelSize; +} + +function _cssVarMap(sprite) { + // For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class, which works as a + // 60x60 image pointing at the proper part of the 90x90 sprite. + // We set up the custom info here, and the template makes use of it. + if (sprite.name.match(/hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears/) || sprite.name=='head_0') { + sprite.custom = { + px: { + offset_x: `-${ sprite.x + 25 }px`, + offset_y: `-${ sprite.y + 15 }px`, + width: '60px', + height: '60px' + } + } + } + if (~sprite.name.indexOf('shirt')) + sprite.custom.px.offset_y = `-${ sprite.y + 30 }px`; // even more for shirts +} diff --git a/vagrant.sh b/vagrant.sh index f44bb6e3b0..84aa22d72f 100644 --- a/vagrant.sh +++ b/vagrant.sh @@ -71,8 +71,8 @@ apt-get install -qq nodejs cd /vagrant -echo Installing grunt/bower... -npm install -g grunt-cli bower phantomjs +echo Installing gulp/bower... +npm install -g gulp grunt-cli bower echo Installing Habitica npm install --no-bin-link