├── .github └── FUNDING.yml ├── .gitignore ├── docs ├── example.png ├── example.pxd ├── images │ ├── fork.png │ ├── github.png │ └── twitter.png ├── retina-images │ ├── fork.png │ ├── fork@2x.png │ ├── github.png │ ├── twitter.png │ ├── github@2x.png │ └── twitter@2x.png ├── examples │ ├── engine │ │ ├── sprite.png │ │ └── sprite.styl │ ├── retina │ │ ├── sprite.png │ │ ├── sprite@2x.png │ │ └── sprite.styl │ ├── padding │ │ ├── sprite.png │ │ └── sprite.styl │ ├── algorithm │ │ ├── sprite.png │ │ └── sprite.styl │ ├── template-function │ │ ├── sprite.png │ │ └── sprite.yml │ ├── handlebars-template │ │ ├── sprite.png │ │ └── sprite.css │ └── handlebars-inheritance │ │ ├── sprite.png │ │ └── sprite.scss ├── handlebarsStr.css.handlebars ├── handlebarsInheritance.scss.handlebars └── gulpfile.js ├── test ├── test-files │ ├── sprite1.png │ ├── sprite2.png │ ├── sprite3.png │ ├── sprite1@2x.png │ ├── sprite2@2x.png │ ├── sprite3@2x.png │ └── scss.template.handlebars ├── expected-files │ ├── retina │ │ ├── pixelsmith.png │ │ ├── pixelsmith@2x.png │ │ └── sprite.css │ ├── default │ │ ├── pixelsmith.png │ │ └── sprite.css │ ├── formats │ │ ├── mint-pngsmith.png │ │ └── sprite.styl │ ├── options │ │ ├── mint-pngsmith.png │ │ └── sprite.css │ ├── two-streams │ │ ├── pixelsmith.png │ │ └── sprite.css │ ├── retina-two-streams │ │ ├── pixelsmith.png │ │ ├── pixelsmith@2x.png │ │ └── sprite.css │ ├── template │ │ ├── mint-graphicsmagick.png │ │ └── sprite.scss │ ├── spritesheet-name │ │ └── sprite.scss │ └── retina-mapped │ │ └── sprite.scss ├── utils │ ├── child.js │ └── image.js ├── gulpfile.js └── gulp-spritesmith_test.js ├── .travis.yml ├── .eslintrc.js ├── UNLICENSE ├── package.json ├── CHANGELOG.md ├── lib └── gulp-spritesmith.js └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://twolfson.com/support-me 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | test/actual-files/ 3 | docs/path/ 4 | -------------------------------------------------------------------------------- /docs/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/example.png -------------------------------------------------------------------------------- /docs/example.pxd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/example.pxd -------------------------------------------------------------------------------- /docs/images/fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/images/fork.png -------------------------------------------------------------------------------- /docs/images/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/images/github.png -------------------------------------------------------------------------------- /docs/images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/images/twitter.png -------------------------------------------------------------------------------- /docs/retina-images/fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/retina-images/fork.png -------------------------------------------------------------------------------- /test/test-files/sprite1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/test-files/sprite1.png -------------------------------------------------------------------------------- /test/test-files/sprite2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/test-files/sprite2.png -------------------------------------------------------------------------------- /test/test-files/sprite3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/test-files/sprite3.png -------------------------------------------------------------------------------- /docs/examples/engine/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/examples/engine/sprite.png -------------------------------------------------------------------------------- /docs/examples/retina/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/examples/retina/sprite.png -------------------------------------------------------------------------------- /docs/retina-images/fork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/retina-images/fork@2x.png -------------------------------------------------------------------------------- /docs/retina-images/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/retina-images/github.png -------------------------------------------------------------------------------- /docs/retina-images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/retina-images/twitter.png -------------------------------------------------------------------------------- /test/test-files/sprite1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/test-files/sprite1@2x.png -------------------------------------------------------------------------------- /test/test-files/sprite2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/test-files/sprite2@2x.png -------------------------------------------------------------------------------- /test/test-files/sprite3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/test-files/sprite3@2x.png -------------------------------------------------------------------------------- /docs/examples/padding/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/examples/padding/sprite.png -------------------------------------------------------------------------------- /docs/retina-images/github@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/retina-images/github@2x.png -------------------------------------------------------------------------------- /docs/retina-images/twitter@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/retina-images/twitter@2x.png -------------------------------------------------------------------------------- /docs/examples/algorithm/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/examples/algorithm/sprite.png -------------------------------------------------------------------------------- /docs/examples/retina/sprite@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/examples/retina/sprite@2x.png -------------------------------------------------------------------------------- /test/expected-files/retina/pixelsmith.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/expected-files/retina/pixelsmith.png -------------------------------------------------------------------------------- /docs/examples/template-function/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/examples/template-function/sprite.png -------------------------------------------------------------------------------- /test/expected-files/default/pixelsmith.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/expected-files/default/pixelsmith.png -------------------------------------------------------------------------------- /docs/examples/handlebars-template/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/examples/handlebars-template/sprite.png -------------------------------------------------------------------------------- /test/expected-files/formats/mint-pngsmith.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/expected-files/formats/mint-pngsmith.png -------------------------------------------------------------------------------- /test/expected-files/options/mint-pngsmith.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/expected-files/options/mint-pngsmith.png -------------------------------------------------------------------------------- /test/expected-files/retina/pixelsmith@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/expected-files/retina/pixelsmith@2x.png -------------------------------------------------------------------------------- /test/expected-files/two-streams/pixelsmith.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/expected-files/two-streams/pixelsmith.png -------------------------------------------------------------------------------- /docs/examples/handlebars-inheritance/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/docs/examples/handlebars-inheritance/sprite.png -------------------------------------------------------------------------------- /test/expected-files/retina-two-streams/pixelsmith.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/expected-files/retina-two-streams/pixelsmith.png -------------------------------------------------------------------------------- /test/expected-files/template/mint-graphicsmagick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/expected-files/template/mint-graphicsmagick.png -------------------------------------------------------------------------------- /test/expected-files/retina-two-streams/pixelsmith@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/gulp.spritesmith/HEAD/test/expected-files/retina-two-streams/pixelsmith@2x.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "16" 4 | - "14" 5 | - "12" 6 | 7 | notifications: 8 | email: 9 | recipients: 10 | - todd@twolfson.com 11 | on_success: change 12 | on_failure: change 13 | -------------------------------------------------------------------------------- /docs/handlebarsStr.css.handlebars: -------------------------------------------------------------------------------- 1 | {{#sprites}} 2 | .icon-{{name}}:before { 3 | display: block; 4 | background-image: url({{{escaped_image}}}); 5 | background-position: {{px.offset_x}} {{px.offset_y}}; 6 | width: {{px.width}}; 7 | height: {{px.height}}; 8 | } 9 | {{/sprites}} 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Inherit from our package 3 | extends: 'eslint-config-twolfson', 4 | 5 | // Configure our environment 6 | // http://eslint.org/docs/user-guide/configuring#specifying-environments 7 | env: { 8 | node: true, 9 | mocha: true 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /test/test-files/scss.template.handlebars: -------------------------------------------------------------------------------- 1 | @mixin sprite($filename) { 2 | 3 | &:before { 4 | content: ""; 5 | display: inline-block; 6 | 7 | {{#sprites}} 8 | @if $filename == {{name}} { 9 | background-image: url({{{escaped_image}}}); 10 | background-position: {{px.offset_x}} {{px.offset_y}}; 11 | height: {{px.height}}; 12 | width: {{px.width}}; 13 | } 14 | {{/sprites}} 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/examples/handlebars-template/sprite.css: -------------------------------------------------------------------------------- 1 | .icon-fork:before { 2 | display: block; 3 | background-image: url(sprite.png); 4 | background-position: 0px 0px; 5 | width: 32px; 6 | height: 32px; 7 | } 8 | .icon-github:before { 9 | display: block; 10 | background-image: url(sprite.png); 11 | background-position: -32px 0px; 12 | width: 32px; 13 | height: 32px; 14 | } 15 | .icon-twitter:before { 16 | display: block; 17 | background-image: url(sprite.png); 18 | background-position: 0px -32px; 19 | width: 32px; 20 | height: 32px; 21 | } 22 | -------------------------------------------------------------------------------- /test/expected-files/template/sprite.scss: -------------------------------------------------------------------------------- 1 | @mixin sprite($filename) { 2 | 3 | &:before { 4 | content: ""; 5 | display: inline-block; 6 | 7 | @if $filename == sprite1 { 8 | background-image: url(sprite.png); 9 | background-position: 0px 0px; 10 | height: 50px; 11 | width: 50px; 12 | } 13 | @if $filename == sprite2 { 14 | background-image: url(sprite.png); 15 | background-position: 0px -50px; 16 | height: 50px; 17 | width: 50px; 18 | } 19 | @if $filename == sprite3 { 20 | background-image: url(sprite.png); 21 | background-position: 0px -100px; 22 | height: 200px; 23 | width: 100px; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/utils/child.js: -------------------------------------------------------------------------------- 1 | // Load in dependencies 2 | var exec = require('child_process').exec; 3 | 4 | // Define our execution helper 5 | exports.run = function (cmd) { 6 | before(function runFn(done) { 7 | exec(cmd, function (err, stdout, stderr) { 8 | if (!err && stderr) { 9 | err = new Error(stderr); 10 | } 11 | done(err); 12 | }); 13 | }); 14 | }; 15 | exports.runSaveError = function (cmd) { 16 | before(function runFn(done) { 17 | var that = this; 18 | exec(cmd, function (err, stdout, stderr) { 19 | if (!err && stderr) { 20 | err = new Error(stderr); 21 | } 22 | that.err = err; 23 | done(); 24 | }); 25 | }); 26 | after(function cleanup() { 27 | delete this.err; 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /docs/handlebarsInheritance.scss.handlebars: -------------------------------------------------------------------------------- 1 | { 2 | // Default options 3 | 'functions': true, 4 | 'variableNameTransforms': ['dasherize'] 5 | } 6 | 7 | {{#extend "scss"}} 8 | {{#content "sprites"}} 9 | {{#each sprites}} 10 | ${{strings.name}}: ({{px.x}}, {{px.y}}, {{px.offset_x}}, {{px.offset_y}}, {{px.width}}, {{px.height}}, {{px.total_width}}, {{px.total_height}}, '{{{escaped_image}}}', '{{name}}', ); 11 | {{/each}} 12 | {{/content}} 13 | {{#content "spritesheet"}} 14 | ${{spritesheet_info.strings.name_sprites}}: ({{#each sprites}}${{strings.name}}, {{/each}}); 15 | ${{spritesheet_info.strings.name}}: ({{spritesheet.px.width}}, {{spritesheet.px.height}}, '{{{spritesheet.escaped_image}}}', ${{spritesheet_info.strings.name_sprites}}, ); 16 | {{/content}} 17 | {{/extend}} 18 | -------------------------------------------------------------------------------- /test/expected-files/default/sprite.css: -------------------------------------------------------------------------------- 1 | /* 2 | Icon classes can be used entirely standalone. They are named after their original file names. 3 | 4 | Example usage in HTML: 5 | 6 | `display: block` sprite: 7 |
8 | 9 | To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 10 | 11 | // CSS 12 | .icon { 13 | display: inline-block; 14 | } 15 | 16 | // HTML 17 | 18 | */ 19 | .icon-sprite1 { 20 | background-image: url(sprite.png); 21 | background-position: -100px 0px; 22 | width: 50px; 23 | height: 50px; 24 | } 25 | .icon-sprite2 { 26 | background-image: url(sprite.png); 27 | background-position: -100px -50px; 28 | width: 50px; 29 | height: 50px; 30 | } 31 | .icon-sprite3 { 32 | background-image: url(sprite.png); 33 | background-position: 0px 0px; 34 | width: 100px; 35 | height: 200px; 36 | } 37 | -------------------------------------------------------------------------------- /test/expected-files/two-streams/sprite.css: -------------------------------------------------------------------------------- 1 | /* 2 | Icon classes can be used entirely standalone. They are named after their original file names. 3 | 4 | Example usage in HTML: 5 | 6 | `display: block` sprite: 7 |
8 | 9 | To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 10 | 11 | // CSS 12 | .icon { 13 | display: inline-block; 14 | } 15 | 16 | // HTML 17 | 18 | */ 19 | .icon-sprite1 { 20 | background-image: url(sprite.png); 21 | background-position: -100px 0px; 22 | width: 50px; 23 | height: 50px; 24 | } 25 | .icon-sprite2 { 26 | background-image: url(sprite.png); 27 | background-position: -100px -50px; 28 | width: 50px; 29 | height: 50px; 30 | } 31 | .icon-sprite3 { 32 | background-image: url(sprite.png); 33 | background-position: 0px 0px; 34 | width: 100px; 35 | height: 200px; 36 | } 37 | -------------------------------------------------------------------------------- /test/expected-files/options/sprite.css: -------------------------------------------------------------------------------- 1 | /* 2 | Icon classes can be used entirely standalone. They are named after their original file names. 3 | 4 | Example usage in HTML: 5 | 6 | `display: block` sprite: 7 |
8 | 9 | To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 10 | 11 | // CSS 12 | .icon { 13 | display: inline-block; 14 | } 15 | 16 | // HTML 17 | 18 | */ 19 | .icon-sprite1 { 20 | background-image: url(../../everywhere.png); 21 | background-position: -150px 0px; 22 | width: 50px; 23 | height: 50px; 24 | } 25 | .icon-sprite2 { 26 | background-image: url(../../everywhere.png); 27 | background-position: -100px -50px; 28 | width: 50px; 29 | height: 50px; 30 | } 31 | .icon-sprite3 { 32 | background-image: url(../../everywhere.png); 33 | background-position: 0px -100px; 34 | width: 100px; 35 | height: 200px; 36 | } 37 | -------------------------------------------------------------------------------- /test/utils/image.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var pngparse = require('pngparse'); 3 | 4 | // Modified from https://github.com/mikolalysenko/get-pixels/blob/2ac98645119244d6e52afcef5fe52cc9300fb27b/node-pixels.js 5 | // due to lack of loading '.jpg' as a '.png' 6 | exports._loadImage = function (filepath, cb) { 7 | fs.readFile(filepath, function (err, buff) { 8 | if (err) { 9 | return cb(err); 10 | } 11 | pngparse.parse(buff, cb); 12 | }); 13 | }; 14 | 15 | exports.loadActual = function (filepath) { 16 | before(function (done) { 17 | var that = this; 18 | exports._loadImage(filepath, function (err, pixels) { 19 | that.actualPixels = pixels; 20 | done(err); 21 | }); 22 | }); 23 | }; 24 | 25 | exports.loadExpected = function (filepath) { 26 | before(function (done) { 27 | var that = this; 28 | exports._loadImage(filepath, function (err, pixels) { 29 | that.expectedPixels = pixels; 30 | done(err); 31 | }); 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /test/expected-files/retina/sprite.css: -------------------------------------------------------------------------------- 1 | /* 2 | Icon classes can be used entirely standalone. They are named after their original file names. 3 | 4 | Example usage in HTML: 5 | 6 | `display: block` sprite: 7 |
8 | 9 | To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 10 | 11 | // CSS 12 | .icon { 13 | display: inline-block; 14 | } 15 | 16 | // HTML 17 | 18 | */ 19 | .icon-sprite1 { 20 | background-image: url(sprite.png); 21 | background-position: -100px 0px; 22 | width: 50px; 23 | height: 50px; 24 | } 25 | .icon-sprite2 { 26 | background-image: url(sprite.png); 27 | background-position: -100px -50px; 28 | width: 50px; 29 | height: 50px; 30 | } 31 | .icon-sprite3 { 32 | background-image: url(sprite.png); 33 | background-position: 0px 0px; 34 | width: 100px; 35 | height: 200px; 36 | } 37 | 38 | @media (-webkit-min-device-pixel-ratio: 2), 39 | (min-resolution: 192dpi) { 40 | .icon-sprite1 { 41 | background-image: url(sprite@2x.png); 42 | background-size: 150px 200px; 43 | } 44 | .icon-sprite2 { 45 | background-image: url(sprite@2x.png); 46 | background-size: 150px 200px; 47 | } 48 | .icon-sprite3 { 49 | background-image: url(sprite@2x.png); 50 | background-size: 150px 200px; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/expected-files/retina-two-streams/sprite.css: -------------------------------------------------------------------------------- 1 | /* 2 | Icon classes can be used entirely standalone. They are named after their original file names. 3 | 4 | Example usage in HTML: 5 | 6 | `display: block` sprite: 7 |
8 | 9 | To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 10 | 11 | // CSS 12 | .icon { 13 | display: inline-block; 14 | } 15 | 16 | // HTML 17 | 18 | */ 19 | .icon-sprite1 { 20 | background-image: url(sprite.png); 21 | background-position: -100px 0px; 22 | width: 50px; 23 | height: 50px; 24 | } 25 | .icon-sprite2 { 26 | background-image: url(sprite.png); 27 | background-position: -100px -50px; 28 | width: 50px; 29 | height: 50px; 30 | } 31 | .icon-sprite3 { 32 | background-image: url(sprite.png); 33 | background-position: 0px 0px; 34 | width: 100px; 35 | height: 200px; 36 | } 37 | 38 | @media (-webkit-min-device-pixel-ratio: 2), 39 | (min-resolution: 192dpi) { 40 | .icon-sprite1 { 41 | background-image: url(sprite@2x.png); 42 | background-size: 150px 200px; 43 | } 44 | .icon-sprite2 { 45 | background-image: url(sprite@2x.png); 46 | background-size: 150px 200px; 47 | } 48 | .icon-sprite3 { 49 | background-image: url(sprite@2x.png); 50 | background-size: 150px 200px; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /docs/examples/template-function/sprite.yml: -------------------------------------------------------------------------------- 1 | fork: 2 | x: 0 3 | 'y': 0 4 | width: 32 5 | height: 32 6 | source_image: /home/todd/github/gulp.spritesmith/docs/images/fork.png 7 | image: sprite.png 8 | total_width: 64 9 | total_height: 64 10 | escaped_image: sprite.png 11 | offset_x: -0.0 12 | offset_y: -0.0 13 | px: 14 | x: 0px 15 | 'y': 0px 16 | offset_x: 0px 17 | offset_y: 0px 18 | height: 32px 19 | width: 32px 20 | total_height: 64px 21 | total_width: 64px 22 | github: 23 | x: 32 24 | 'y': 0 25 | width: 32 26 | height: 32 27 | source_image: /home/todd/github/gulp.spritesmith/docs/images/github.png 28 | image: sprite.png 29 | total_width: 64 30 | total_height: 64 31 | escaped_image: sprite.png 32 | offset_x: -32 33 | offset_y: -0.0 34 | px: 35 | x: 32px 36 | 'y': 0px 37 | offset_x: '-32px' 38 | offset_y: 0px 39 | height: 32px 40 | width: 32px 41 | total_height: 64px 42 | total_width: 64px 43 | twitter: 44 | x: 0 45 | 'y': 32 46 | width: 32 47 | height: 32 48 | source_image: /home/todd/github/gulp.spritesmith/docs/images/twitter.png 49 | image: sprite.png 50 | total_width: 64 51 | total_height: 64 52 | escaped_image: sprite.png 53 | offset_x: -0.0 54 | offset_y: -32 55 | px: 56 | x: 0px 57 | 'y': 32px 58 | offset_x: 0px 59 | offset_y: '-32px' 60 | height: 32px 61 | width: 32px 62 | total_height: 64px 63 | total_width: 64px 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp.spritesmith", 3 | "description": "Convert a set of images into a spritesheet and CSS variables via gulp", 4 | "version": "6.13.1", 5 | "homepage": "https://github.com/twolfson/gulp.spritesmith", 6 | "author": { 7 | "name": "Todd Wolfson", 8 | "email": "todd@twolfson.com", 9 | "url": "http://twolfson.com/" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/twolfson/gulp.spritesmith.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/twolfson/gulp.spritesmith/issues" 17 | }, 18 | "license": "Unlicense", 19 | "main": "lib/gulp-spritesmith", 20 | "engines": { 21 | "node": ">= 4.0.0" 22 | }, 23 | "scripts": { 24 | "precheck": "eslint docs/ lib/ test/", 25 | "lint": "eslint docs/ lib/ test/ --max-warnings 0", 26 | "test": "npm run precheck && npm run test-mocha && npm run lint", 27 | "test-mocha": "cd test && mocha . --timeout 60000" 28 | }, 29 | "dependencies": { 30 | "async": "~3.2.3", 31 | "minimatch": "~3.0.3", 32 | "spritesheet-templates": "^10.3.0", 33 | "spritesmith": "^3.4.0", 34 | "through2": "~2.0.3", 35 | "underscore": "~1.13.1", 36 | "url2": "~1.0.4", 37 | "vinyl": "~2.1.0" 38 | }, 39 | "devDependencies": { 40 | "eslint": "~4.10.0", 41 | "eslint-config-twolfson": "~1.0.0", 42 | "foundry": "~4.3.2", 43 | "foundry-release-git": "~2.0.2", 44 | "foundry-release-npm": "~2.0.2", 45 | "gulp": "~5.0.0", 46 | "gulp-csso": "~3.0.0", 47 | "gulp-imagemin": "~3.1.1", 48 | "merge-stream": "~1.0.1", 49 | "mocha": "~8.4.0", 50 | "phantomjssmith": "~1.0.0", 51 | "pngparse": "~2.0.1", 52 | "rimraf": "~2.6.1", 53 | "vinyl-buffer": "~1.0.0" 54 | }, 55 | "keywords": [ 56 | "gulpplugin", 57 | "spritesmith", 58 | "sprite", 59 | "spritesheet" 60 | ], 61 | "foundry": { 62 | "releaseCommands": [ 63 | "foundry-release-git", 64 | "foundry-release-npm" 65 | ] 66 | } 67 | } -------------------------------------------------------------------------------- /docs/examples/handlebars-inheritance/sprite.scss: -------------------------------------------------------------------------------- 1 | // SCSS variables are information about icon's compiled state, stored under its original file name 2 | // 3 | // .icon-home { 4 | // width: $icon-home-width; 5 | // } 6 | // 7 | // The large array-like variables contain all information about a single icon 8 | // $icon-home: x y offset_x offset_y width height total_width total_height image_path; 9 | // 10 | // At the bottom of this section, we provide information about the spritesheet itself 11 | // $spritesheet: width height image $spritesheet-sprites; 12 | $fork: (0px, 0px, 0px, 0px, 32px, 32px, 64px, 64px, 'sprite.png', 'fork', ); 13 | $github: (32px, 0px, -32px, 0px, 32px, 32px, 64px, 64px, 'sprite.png', 'github', ); 14 | $twitter: (0px, 32px, 0px, -32px, 32px, 32px, 64px, 64px, 'sprite.png', 'twitter', ); 15 | $spritesheet-sprites: ($fork, $github, $twitter, ); 16 | $spritesheet: (64px, 64px, 'sprite.png', $spritesheet-sprites, ); 17 | 18 | // The provided mixins are intended to be used with the array-like variables 19 | // 20 | // .icon-home { 21 | // @include sprite-width($icon-home); 22 | // } 23 | // 24 | // .icon-email { 25 | // @include sprite($icon-email); 26 | // } 27 | // 28 | // Example usage in HTML: 29 | // 30 | // `display: block` sprite: 31 | //
32 | // 33 | // To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 34 | // 35 | // // CSS 36 | // .icon { 37 | // display: inline-block; 38 | // } 39 | // 40 | // // HTML 41 | // 42 | @mixin sprite-width($sprite) { 43 | width: nth($sprite, 5); 44 | } 45 | 46 | @mixin sprite-height($sprite) { 47 | height: nth($sprite, 6); 48 | } 49 | 50 | @mixin sprite-position($sprite) { 51 | $sprite-offset-x: nth($sprite, 3); 52 | $sprite-offset-y: nth($sprite, 4); 53 | background-position: $sprite-offset-x $sprite-offset-y; 54 | } 55 | 56 | @mixin sprite-image($sprite) { 57 | $sprite-image: nth($sprite, 9); 58 | background-image: url(#{$sprite-image}); 59 | } 60 | 61 | @mixin sprite($sprite) { 62 | @include sprite-image($sprite); 63 | @include sprite-position($sprite); 64 | @include sprite-width($sprite); 65 | @include sprite-height($sprite); 66 | } 67 | 68 | // The `sprites` mixin generates identical output to the CSS template 69 | // but can be overridden inside of SCSS 70 | // 71 | // @include sprites($spritesheet-sprites); 72 | @mixin sprites($sprites) { 73 | @each $sprite in $sprites { 74 | $sprite-name: nth($sprite, 10); 75 | .#{$sprite-name} { 76 | @include sprite($sprite); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /docs/examples/engine/sprite.styl: -------------------------------------------------------------------------------- 1 | /* 2 | Stylus variables are information about icon's compiled state, stored under its original file name 3 | 4 | .icon-home { 5 | width: $icon_home_width; 6 | } 7 | 8 | The large array-like variables contain all information about a single icon 9 | $icon_home = x y offset_x offset_y width height total_width total_height image_path; 10 | 11 | At the bottom of this section, we provide information about the spritesheet itself 12 | $spritesheet = width height image $spritesheet_sprites; 13 | */ 14 | $fork_name = 'fork'; 15 | $fork_x = 0px; 16 | $fork_y = 0px; 17 | $fork_offset_x = 0px; 18 | $fork_offset_y = 0px; 19 | $fork_width = 32px; 20 | $fork_height = 32px; 21 | $fork_total_width = 64px; 22 | $fork_total_height = 64px; 23 | $fork_image = 'sprite.png'; 24 | $fork = 0px 0px 0px 0px 32px 32px 64px 64px 'sprite.png' 'fork'; 25 | $github_name = 'github'; 26 | $github_x = 32px; 27 | $github_y = 0px; 28 | $github_offset_x = -32px; 29 | $github_offset_y = 0px; 30 | $github_width = 32px; 31 | $github_height = 32px; 32 | $github_total_width = 64px; 33 | $github_total_height = 64px; 34 | $github_image = 'sprite.png'; 35 | $github = 32px 0px -32px 0px 32px 32px 64px 64px 'sprite.png' 'github'; 36 | $twitter_name = 'twitter'; 37 | $twitter_x = 0px; 38 | $twitter_y = 32px; 39 | $twitter_offset_x = 0px; 40 | $twitter_offset_y = -32px; 41 | $twitter_width = 32px; 42 | $twitter_height = 32px; 43 | $twitter_total_width = 64px; 44 | $twitter_total_height = 64px; 45 | $twitter_image = 'sprite.png'; 46 | $twitter = 0px 32px 0px -32px 32px 32px 64px 64px 'sprite.png' 'twitter'; 47 | $spritesheet_width = 64px; 48 | $spritesheet_height = 64px; 49 | $spritesheet_image = 'sprite.png'; 50 | $spritesheet_sprites = $fork $github $twitter; 51 | $spritesheet = 64px 64px 'sprite.png' $spritesheet_sprites; 52 | 53 | /* 54 | The provided mixins are intended to be used with the array-like variables 55 | 56 | .icon-home { 57 | spriteWidth($icon_home) 58 | } 59 | 60 | .icon-email { 61 | sprite($icon_email) 62 | } 63 | 64 | Example usage in HTML: 65 | 66 | `display: block` sprite: 67 |
68 | 69 | To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 70 | 71 | // CSS 72 | .icon { 73 | display: inline-block; 74 | } 75 | 76 | // HTML 77 | 78 | */ 79 | spriteWidth($sprite) { 80 | width: $sprite[4]; 81 | } 82 | 83 | spriteHeight($sprite) { 84 | height: $sprite[5]; 85 | } 86 | 87 | spritePosition($sprite) { 88 | background-position: $sprite[2] $sprite[3]; 89 | } 90 | 91 | spriteImage($sprite) { 92 | background-image: url($sprite[8]); 93 | } 94 | 95 | sprite($sprite) { 96 | spriteImage($sprite) 97 | spritePosition($sprite) 98 | spriteWidth($sprite) 99 | spriteHeight($sprite) 100 | } 101 | 102 | /* 103 | The `sprites` mixin generates identical output to the CSS template 104 | but can be overridden inside of Stylus 105 | 106 | This must be run when you have at least 2 sprites. 107 | If run with a single sprite, then there will be reference errors. 108 | 109 | sprites($spritesheet_sprites); 110 | */ 111 | sprites($sprites) { 112 | for $sprite in $sprites { 113 | $sprite_name = $sprite[9]; 114 | .{$sprite_name} { 115 | sprite($sprite); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /docs/examples/padding/sprite.styl: -------------------------------------------------------------------------------- 1 | /* 2 | Stylus variables are information about icon's compiled state, stored under its original file name 3 | 4 | .icon-home { 5 | width: $icon_home_width; 6 | } 7 | 8 | The large array-like variables contain all information about a single icon 9 | $icon_home = x y offset_x offset_y width height total_width total_height image_path; 10 | 11 | At the bottom of this section, we provide information about the spritesheet itself 12 | $spritesheet = width height image $spritesheet_sprites; 13 | */ 14 | $fork_name = 'fork'; 15 | $fork_x = 0px; 16 | $fork_y = 0px; 17 | $fork_offset_x = 0px; 18 | $fork_offset_y = 0px; 19 | $fork_width = 32px; 20 | $fork_height = 32px; 21 | $fork_total_width = 84px; 22 | $fork_total_height = 84px; 23 | $fork_image = 'sprite.png'; 24 | $fork = 0px 0px 0px 0px 32px 32px 84px 84px 'sprite.png' 'fork'; 25 | $github_name = 'github'; 26 | $github_x = 52px; 27 | $github_y = 0px; 28 | $github_offset_x = -52px; 29 | $github_offset_y = 0px; 30 | $github_width = 32px; 31 | $github_height = 32px; 32 | $github_total_width = 84px; 33 | $github_total_height = 84px; 34 | $github_image = 'sprite.png'; 35 | $github = 52px 0px -52px 0px 32px 32px 84px 84px 'sprite.png' 'github'; 36 | $twitter_name = 'twitter'; 37 | $twitter_x = 0px; 38 | $twitter_y = 52px; 39 | $twitter_offset_x = 0px; 40 | $twitter_offset_y = -52px; 41 | $twitter_width = 32px; 42 | $twitter_height = 32px; 43 | $twitter_total_width = 84px; 44 | $twitter_total_height = 84px; 45 | $twitter_image = 'sprite.png'; 46 | $twitter = 0px 52px 0px -52px 32px 32px 84px 84px 'sprite.png' 'twitter'; 47 | $spritesheet_width = 84px; 48 | $spritesheet_height = 84px; 49 | $spritesheet_image = 'sprite.png'; 50 | $spritesheet_sprites = $fork $github $twitter; 51 | $spritesheet = 84px 84px 'sprite.png' $spritesheet_sprites; 52 | 53 | /* 54 | The provided mixins are intended to be used with the array-like variables 55 | 56 | .icon-home { 57 | spriteWidth($icon_home) 58 | } 59 | 60 | .icon-email { 61 | sprite($icon_email) 62 | } 63 | 64 | Example usage in HTML: 65 | 66 | `display: block` sprite: 67 |
68 | 69 | To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 70 | 71 | // CSS 72 | .icon { 73 | display: inline-block; 74 | } 75 | 76 | // HTML 77 | 78 | */ 79 | spriteWidth($sprite) { 80 | width: $sprite[4]; 81 | } 82 | 83 | spriteHeight($sprite) { 84 | height: $sprite[5]; 85 | } 86 | 87 | spritePosition($sprite) { 88 | background-position: $sprite[2] $sprite[3]; 89 | } 90 | 91 | spriteImage($sprite) { 92 | background-image: url($sprite[8]); 93 | } 94 | 95 | sprite($sprite) { 96 | spriteImage($sprite) 97 | spritePosition($sprite) 98 | spriteWidth($sprite) 99 | spriteHeight($sprite) 100 | } 101 | 102 | /* 103 | The `sprites` mixin generates identical output to the CSS template 104 | but can be overridden inside of Stylus 105 | 106 | This must be run when you have at least 2 sprites. 107 | If run with a single sprite, then there will be reference errors. 108 | 109 | sprites($spritesheet_sprites); 110 | */ 111 | sprites($sprites) { 112 | for $sprite in $sprites { 113 | $sprite_name = $sprite[9]; 114 | .{$sprite_name} { 115 | sprite($sprite); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /docs/examples/algorithm/sprite.styl: -------------------------------------------------------------------------------- 1 | /* 2 | Stylus variables are information about icon's compiled state, stored under its original file name 3 | 4 | .icon-home { 5 | width: $icon_home_width; 6 | } 7 | 8 | The large array-like variables contain all information about a single icon 9 | $icon_home = x y offset_x offset_y width height total_width total_height image_path; 10 | 11 | At the bottom of this section, we provide information about the spritesheet itself 12 | $spritesheet = width height image $spritesheet_sprites; 13 | */ 14 | $fork_name = 'fork'; 15 | $fork_x = 64px; 16 | $fork_y = 0px; 17 | $fork_offset_x = -64px; 18 | $fork_offset_y = 0px; 19 | $fork_width = 32px; 20 | $fork_height = 32px; 21 | $fork_total_width = 96px; 22 | $fork_total_height = 96px; 23 | $fork_image = 'sprite.png'; 24 | $fork = 64px 0px -64px 0px 32px 32px 96px 96px 'sprite.png' 'fork'; 25 | $github_name = 'github'; 26 | $github_x = 32px; 27 | $github_y = 32px; 28 | $github_offset_x = -32px; 29 | $github_offset_y = -32px; 30 | $github_width = 32px; 31 | $github_height = 32px; 32 | $github_total_width = 96px; 33 | $github_total_height = 96px; 34 | $github_image = 'sprite.png'; 35 | $github = 32px 32px -32px -32px 32px 32px 96px 96px 'sprite.png' 'github'; 36 | $twitter_name = 'twitter'; 37 | $twitter_x = 0px; 38 | $twitter_y = 64px; 39 | $twitter_offset_x = 0px; 40 | $twitter_offset_y = -64px; 41 | $twitter_width = 32px; 42 | $twitter_height = 32px; 43 | $twitter_total_width = 96px; 44 | $twitter_total_height = 96px; 45 | $twitter_image = 'sprite.png'; 46 | $twitter = 0px 64px 0px -64px 32px 32px 96px 96px 'sprite.png' 'twitter'; 47 | $spritesheet_width = 96px; 48 | $spritesheet_height = 96px; 49 | $spritesheet_image = 'sprite.png'; 50 | $spritesheet_sprites = $fork $github $twitter; 51 | $spritesheet = 96px 96px 'sprite.png' $spritesheet_sprites; 52 | 53 | /* 54 | The provided mixins are intended to be used with the array-like variables 55 | 56 | .icon-home { 57 | spriteWidth($icon_home) 58 | } 59 | 60 | .icon-email { 61 | sprite($icon_email) 62 | } 63 | 64 | Example usage in HTML: 65 | 66 | `display: block` sprite: 67 |
68 | 69 | To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 70 | 71 | // CSS 72 | .icon { 73 | display: inline-block; 74 | } 75 | 76 | // HTML 77 | 78 | */ 79 | spriteWidth($sprite) { 80 | width: $sprite[4]; 81 | } 82 | 83 | spriteHeight($sprite) { 84 | height: $sprite[5]; 85 | } 86 | 87 | spritePosition($sprite) { 88 | background-position: $sprite[2] $sprite[3]; 89 | } 90 | 91 | spriteImage($sprite) { 92 | background-image: url($sprite[8]); 93 | } 94 | 95 | sprite($sprite) { 96 | spriteImage($sprite) 97 | spritePosition($sprite) 98 | spriteWidth($sprite) 99 | spriteHeight($sprite) 100 | } 101 | 102 | /* 103 | The `sprites` mixin generates identical output to the CSS template 104 | but can be overridden inside of Stylus 105 | 106 | This must be run when you have at least 2 sprites. 107 | If run with a single sprite, then there will be reference errors. 108 | 109 | sprites($spritesheet_sprites); 110 | */ 111 | sprites($sprites) { 112 | for $sprite in $sprites { 113 | $sprite_name = $sprite[9]; 114 | .{$sprite_name} { 115 | sprite($sprite); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /test/expected-files/formats/sprite.styl: -------------------------------------------------------------------------------- 1 | /* 2 | Stylus variables are information about icon's compiled state, stored under its original file name 3 | 4 | .icon-home { 5 | width: $icon_home_width; 6 | } 7 | 8 | The large array-like variables contain all information about a single icon 9 | $icon_home = x y offset_x offset_y width height total_width total_height image_path; 10 | 11 | At the bottom of this section, we provide information about the spritesheet itself 12 | $spritesheet = width height image $spritesheet_sprites; 13 | */ 14 | $sprite1_name = 'sprite1'; 15 | $sprite1_x = 0px; 16 | $sprite1_y = 0px; 17 | $sprite1_offset_x = 0px; 18 | $sprite1_offset_y = 0px; 19 | $sprite1_width = 50px; 20 | $sprite1_height = 50px; 21 | $sprite1_total_width = 100px; 22 | $sprite1_total_height = 300px; 23 | $sprite1_image = 'sprite.jpg'; 24 | $sprite1 = 0px 0px 0px 0px 50px 50px 100px 300px 'sprite.jpg' 'sprite1'; 25 | $sprite2_name = 'sprite2'; 26 | $sprite2_x = 0px; 27 | $sprite2_y = 50px; 28 | $sprite2_offset_x = 0px; 29 | $sprite2_offset_y = -50px; 30 | $sprite2_width = 50px; 31 | $sprite2_height = 50px; 32 | $sprite2_total_width = 100px; 33 | $sprite2_total_height = 300px; 34 | $sprite2_image = 'sprite.jpg'; 35 | $sprite2 = 0px 50px 0px -50px 50px 50px 100px 300px 'sprite.jpg' 'sprite2'; 36 | $sprite3_name = 'sprite3'; 37 | $sprite3_x = 0px; 38 | $sprite3_y = 100px; 39 | $sprite3_offset_x = 0px; 40 | $sprite3_offset_y = -100px; 41 | $sprite3_width = 100px; 42 | $sprite3_height = 200px; 43 | $sprite3_total_width = 100px; 44 | $sprite3_total_height = 300px; 45 | $sprite3_image = 'sprite.jpg'; 46 | $sprite3 = 0px 100px 0px -100px 100px 200px 100px 300px 'sprite.jpg' 'sprite3'; 47 | $spritesheet_width = 100px; 48 | $spritesheet_height = 300px; 49 | $spritesheet_image = 'sprite.jpg'; 50 | $spritesheet_sprites = $sprite1 $sprite2 $sprite3; 51 | $spritesheet = 100px 300px 'sprite.jpg' $spritesheet_sprites; 52 | 53 | /* 54 | The provided mixins are intended to be used with the array-like variables 55 | 56 | .icon-home { 57 | spriteWidth($icon_home) 58 | } 59 | 60 | .icon-email { 61 | sprite($icon_email) 62 | } 63 | 64 | Example usage in HTML: 65 | 66 | `display: block` sprite: 67 |
68 | 69 | To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 70 | 71 | // CSS 72 | .icon { 73 | display: inline-block; 74 | } 75 | 76 | // HTML 77 | 78 | */ 79 | spriteWidth($sprite) { 80 | width: $sprite[4]; 81 | } 82 | 83 | spriteHeight($sprite) { 84 | height: $sprite[5]; 85 | } 86 | 87 | spritePosition($sprite) { 88 | background-position: $sprite[2] $sprite[3]; 89 | } 90 | 91 | spriteImage($sprite) { 92 | background-image: url($sprite[8]); 93 | } 94 | 95 | sprite($sprite) { 96 | spriteImage($sprite) 97 | spritePosition($sprite) 98 | spriteWidth($sprite) 99 | spriteHeight($sprite) 100 | } 101 | 102 | /* 103 | The `sprites` mixin generates identical output to the CSS template 104 | but can be overridden inside of Stylus 105 | 106 | This must be run when you have at least 2 sprites. 107 | If run with a single sprite, then there will be reference errors. 108 | 109 | sprites($spritesheet_sprites); 110 | */ 111 | sprites($sprites) { 112 | for $sprite in $sprites { 113 | $sprite_name = $sprite[9]; 114 | .{$sprite_name} { 115 | sprite($sprite); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /test/expected-files/spritesheet-name/sprite.scss: -------------------------------------------------------------------------------- 1 | // SCSS variables are information about icon's compiled state, stored under its original file name 2 | // 3 | // .icon-home { 4 | // width: $icon-home-width; 5 | // } 6 | // 7 | // The large array-like variables contain all information about a single icon 8 | // $icon-home: x y offset_x offset_y width height total_width total_height image_path; 9 | // 10 | // At the bottom of this section, we provide information about the spritesheet itself 11 | // $spritesheet: width height image $spritesheet-sprites; 12 | $sprite1-name: 'sprite1'; 13 | $sprite1-x: 0px; 14 | $sprite1-y: 0px; 15 | $sprite1-offset-x: 0px; 16 | $sprite1-offset-y: 0px; 17 | $sprite1-width: 50px; 18 | $sprite1-height: 50px; 19 | $sprite1-total-width: 100px; 20 | $sprite1-total-height: 300px; 21 | $sprite1-image: 'sprite.png'; 22 | $sprite1: (0px, 0px, 0px, 0px, 50px, 50px, 100px, 300px, 'sprite.png', 'sprite1', ); 23 | $sprite2-name: 'sprite2'; 24 | $sprite2-x: 0px; 25 | $sprite2-y: 50px; 26 | $sprite2-offset-x: 0px; 27 | $sprite2-offset-y: -50px; 28 | $sprite2-width: 50px; 29 | $sprite2-height: 50px; 30 | $sprite2-total-width: 100px; 31 | $sprite2-total-height: 300px; 32 | $sprite2-image: 'sprite.png'; 33 | $sprite2: (0px, 50px, 0px, -50px, 50px, 50px, 100px, 300px, 'sprite.png', 'sprite2', ); 34 | $sprite3-name: 'sprite3'; 35 | $sprite3-x: 0px; 36 | $sprite3-y: 100px; 37 | $sprite3-offset-x: 0px; 38 | $sprite3-offset-y: -100px; 39 | $sprite3-width: 100px; 40 | $sprite3-height: 200px; 41 | $sprite3-total-width: 100px; 42 | $sprite3-total-height: 300px; 43 | $sprite3-image: 'sprite.png'; 44 | $sprite3: (0px, 100px, 0px, -100px, 100px, 200px, 100px, 300px, 'sprite.png', 'sprite3', ); 45 | $icons-width: 100px; 46 | $icons-height: 300px; 47 | $icons-image: 'sprite.png'; 48 | $icons-sprites: ($sprite1, $sprite2, $sprite3, ); 49 | $icons: (100px, 300px, 'sprite.png', $icons-sprites, ); 50 | 51 | // The provided mixins are intended to be used with the array-like variables 52 | // 53 | // .icon-home { 54 | // @include sprite-width($icon-home); 55 | // } 56 | // 57 | // .icon-email { 58 | // @include sprite($icon-email); 59 | // } 60 | // 61 | // Example usage in HTML: 62 | // 63 | // `display: block` sprite: 64 | //
65 | // 66 | // To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 67 | // 68 | // // CSS 69 | // .icon { 70 | // display: inline-block; 71 | // } 72 | // 73 | // // HTML 74 | // 75 | @mixin sprite-width($sprite) { 76 | width: nth($sprite, 5); 77 | } 78 | 79 | @mixin sprite-height($sprite) { 80 | height: nth($sprite, 6); 81 | } 82 | 83 | @mixin sprite-position($sprite) { 84 | $sprite-offset-x: nth($sprite, 3); 85 | $sprite-offset-y: nth($sprite, 4); 86 | background-position: $sprite-offset-x $sprite-offset-y; 87 | } 88 | 89 | @mixin sprite-image($sprite) { 90 | $sprite-image: nth($sprite, 9); 91 | background-image: url(#{$sprite-image}); 92 | } 93 | 94 | @mixin sprite($sprite) { 95 | @include sprite-image($sprite); 96 | @include sprite-position($sprite); 97 | @include sprite-width($sprite); 98 | @include sprite-height($sprite); 99 | } 100 | 101 | // The `sprites` mixin generates identical output to the CSS template 102 | // but can be overridden inside of SCSS 103 | // 104 | // @include sprites($spritesheet-sprites); 105 | @mixin sprites($sprites) { 106 | @each $sprite in $sprites { 107 | $sprite-name: nth($sprite, 10); 108 | .#{$sprite-name} { 109 | @include sprite($sprite); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /docs/gulpfile.js: -------------------------------------------------------------------------------- 1 | // Load in dependencies 2 | var gulp = require('gulp'); 3 | var buffer = require('vinyl-buffer'); 4 | var csso = require('gulp-csso'); 5 | var imagemin = require('gulp-imagemin'); 6 | var merge = require('merge-stream'); 7 | var phantomjssmith = require('phantomjssmith'); 8 | var yaml = require('js-yaml'); 9 | var spritesmith = require('../'); 10 | 11 | // Define our tasks 12 | gulp.task('sprite', function () { 13 | // Collect png's from images folder and output a .png spritesheet and CSS classes 14 | // Alternative outputs include: SASS, Stylus, LESS, JSON 15 | var spriteData = gulp.src('images/*.png').pipe(spritesmith({ 16 | imgName: 'sprite.png', 17 | cssName: 'sprite.css', 18 | algorithm: 'binary-tree' 19 | })); 20 | return spriteData.pipe(gulp.dest('path/to/output/')); 21 | }); 22 | 23 | gulp.task('sprite-pipeline', function () { 24 | // Generate our spritesheet 25 | var spriteData = gulp.src('images/*.png').pipe(spritesmith({ 26 | imgName: 'sprite.png', 27 | cssName: 'sprite.css' 28 | })); 29 | 30 | // Pipe image stream through image optimizer and onto disk 31 | var imgStream = spriteData.img 32 | // DEV: We must buffer our stream into a Buffer for `imagemin` 33 | .pipe(buffer()) 34 | .pipe(imagemin()) 35 | .pipe(gulp.dest('path/to/image/folder/')); 36 | 37 | // Pipe CSS stream through CSS optimizer and onto disk 38 | var cssStream = spriteData.css 39 | .pipe(csso()) 40 | .pipe(gulp.dest('path/to/css/folder/')); 41 | 42 | // Return a merged stream to handle both `end` events 43 | return merge(imgStream, cssStream); 44 | }); 45 | 46 | gulp.task('sprite-algorithm', function () { 47 | var spriteData = gulp.src('images/*.png').pipe(spritesmith({ 48 | imgName: 'sprite.png', 49 | cssName: 'sprite.styl', 50 | algorithm: 'alt-diagonal' 51 | })); 52 | return spriteData.pipe(gulp.dest('examples/algorithm/')); 53 | }); 54 | 55 | gulp.task('sprite-engine', function () { 56 | var spriteData = gulp.src('images/*.png', {read: false}).pipe(spritesmith({ 57 | imgName: 'sprite.png', 58 | cssName: 'sprite.styl', 59 | engine: phantomjssmith 60 | })); 61 | return spriteData.pipe(gulp.dest('examples/engine/')); 62 | }); 63 | 64 | gulp.task('sprite-padding', function () { 65 | var spriteData = gulp.src('images/*.png').pipe(spritesmith({ 66 | imgName: 'sprite.png', 67 | cssName: 'sprite.styl', 68 | padding: 20 // Exaggerated for visibility, normal usage is 1 or 2 69 | })); 70 | return spriteData.pipe(gulp.dest('examples/padding/')); 71 | }); 72 | 73 | gulp.task('sprite-retina', function () { 74 | var spriteData = gulp.src('retina-images/*.png').pipe(spritesmith({ 75 | // This will filter out `fork@2x.png`, `github@2x.png`, ... for our retina spritesheet 76 | // The normal spritesheet will now receive `fork.png`, `github.png`, ... 77 | retinaSrcFilter: ['retina-images/*@2x.png'], 78 | imgName: 'sprite.png', 79 | retinaImgName: 'sprite@2x.png', 80 | cssName: 'sprite.styl' 81 | })); 82 | return spriteData.pipe(gulp.dest('examples/retina/')); 83 | }); 84 | 85 | gulp.task('sprite-handlebars-template', function () { 86 | var spriteData = gulp.src('images/*.png').pipe(spritesmith({ 87 | imgName: 'sprite.png', 88 | cssName: 'sprite.css', 89 | cssTemplate: 'handlebarsStr.css.handlebars' 90 | })); 91 | return spriteData.pipe(gulp.dest('examples/handlebars-template/')); 92 | }); 93 | 94 | gulp.task('sprite-handlebars-inheritance', function () { 95 | var spriteData = gulp.src('images/*.png').pipe(spritesmith({ 96 | imgName: 'sprite.png', 97 | cssName: 'sprite.scss', 98 | cssTemplate: 'handlebarsInheritance.scss.handlebars' 99 | })); 100 | return spriteData.pipe(gulp.dest('examples/handlebars-inheritance/')); 101 | }); 102 | 103 | gulp.task('sprite-template-function', function () { 104 | var spriteData = gulp.src('images/*.png').pipe(spritesmith({ 105 | imgName: 'sprite.png', 106 | cssName: 'sprite.yml', 107 | cssTemplate: function (data) { 108 | // Convert sprites from an array into an object 109 | var spriteObj = {}; 110 | data.sprites.forEach(function (sprite) { 111 | // Grab the name and store the sprite under it 112 | var name = sprite.name; 113 | spriteObj[name] = sprite; 114 | 115 | // Delete the name from the sprite 116 | delete sprite.name; 117 | }); 118 | 119 | // Return stringified spriteObj 120 | return yaml.safeDump(spriteObj); 121 | } 122 | })); 123 | return spriteData.pipe(gulp.dest('examples/template-function/')); 124 | }); 125 | 126 | // Define common task for all 127 | gulp.task('sprite-all', [ 128 | 'sprite', 129 | 'sprite-pipeline', 130 | 'sprite-algorithm', 131 | 'sprite-engine', 132 | 'sprite-padding', 133 | 'sprite-retina', 134 | 'sprite-handlebars-template', 135 | 'sprite-handlebars-inheritance', 136 | 'sprite-template-function' 137 | ]); 138 | -------------------------------------------------------------------------------- /test/gulpfile.js: -------------------------------------------------------------------------------- 1 | // Load in dependencies 2 | var gulp = require('gulp'); 3 | var merge = require('merge-stream'); 4 | var through2 = require('through2'); 5 | var spritesmith = require('../'); 6 | 7 | // Define our test tasks 8 | var images = [ 9 | 'test-files/sprite1.png', 10 | 'test-files/sprite2.png', 11 | 'test-files/sprite3.png' 12 | ]; 13 | var retinaImages = [ 14 | 'test-files/sprite1.png', 15 | 'test-files/sprite1@2x.png', 16 | 'test-files/sprite2.png', 17 | 'test-files/sprite2@2x.png', 18 | 'test-files/sprite3.png', 19 | 'test-files/sprite3@2x.png' 20 | ]; 21 | gulp.task('sprite-default', function () { 22 | return gulp.src(images, {encoding: false}) 23 | .pipe(spritesmith({ 24 | imgName: 'sprite.png', 25 | cssName: 'sprite.css' 26 | })) 27 | .pipe(gulp.dest('actual-files/default/', {encoding: false})); 28 | }); 29 | 30 | gulp.task('sprite-retina', function () { 31 | return gulp.src(retinaImages, {encoding: false}) 32 | .pipe(spritesmith({ 33 | retinaSrcFilter: 'test-files/*@2x.png', 34 | imgName: 'sprite.png', 35 | retinaImgName: 'sprite@2x.png', 36 | cssName: 'sprite.css' 37 | })) 38 | .pipe(gulp.dest('actual-files/retina/', {encoding: false})); 39 | }); 40 | 41 | gulp.task('sprite-two-streams', function () { 42 | var data = gulp.src(images, {encoding: false}) 43 | .pipe(spritesmith({ 44 | imgName: 'sprite.png', 45 | cssName: 'sprite.css' 46 | })); 47 | var imgStream = data.img.pipe(gulp.dest('actual-files/two-streams/', {encoding: false})); 48 | var cssStream = data.css.pipe(gulp.dest('actual-files/two-streams/')); 49 | return merge(imgStream, cssStream); 50 | }); 51 | 52 | gulp.task('sprite-retina-two-streams', function () { 53 | var data = gulp.src(retinaImages, {encoding: false}).pipe(spritesmith({ 54 | retinaSrcFilter: 'test-files/*@2x.png', 55 | imgName: 'sprite.png', 56 | retinaImgName: 'sprite@2x.png', 57 | cssName: 'sprite.css' 58 | })); 59 | var imgStream = data.img.pipe(gulp.dest('actual-files/retina-two-streams/', {encoding: false})); 60 | var cssStream = data.css.pipe(gulp.dest('actual-files/retina-two-streams/')); 61 | return merge(imgStream, cssStream); 62 | }); 63 | 64 | gulp.task('sprite-retina-same-name', function () { 65 | return gulp.src(retinaImages, {encoding: false}) 66 | .pipe(spritesmith({ 67 | retinaSrcFilter: 'test-files/*@2x.png', 68 | imgName: 'sprite.png', 69 | retinaImgName: 'sprite@2x.png', 70 | cssVarMap: function (sprite) { 71 | // Coerce all 1x and 2x sprites to same name 72 | // DEV: This emulates `1x/icon.png` and `2x/icon.png` folders 73 | sprite.name = sprite.name.replace('@2x', ''); 74 | }, 75 | cssName: 'sprite.css' 76 | })) 77 | .pipe(gulp.dest('actual-files/retina-same-name/', {encoding: false})); 78 | }); 79 | 80 | gulp.task('sprite-formats', function () { 81 | return gulp.src(images, {read: false, encoding: false}) 82 | .pipe(spritesmith({ 83 | imgName: 'sprite.jpg', 84 | cssName: 'sprite.css', 85 | imgOpts: { 86 | format: 'png' 87 | }, 88 | cssFormat: 'stylus', 89 | engine: 'phantomjssmith', 90 | // Use `top-down` for easier testing 91 | algorithm: 'top-down' 92 | })) 93 | .pipe(gulp.dest('actual-files/formats/', {encoding: false})); 94 | }); 95 | 96 | gulp.task('sprite-options', function () { 97 | return gulp.src(images, {encoding: false}) 98 | .pipe(spritesmith({ 99 | imgName: 'sprite.png', 100 | cssName: 'sprite.css', 101 | imgPath: '../../everywhere.png', 102 | algorithm: 'alt-diagonal' 103 | })) 104 | .pipe(gulp.dest('actual-files/options/', {encoding: false})); 105 | }); 106 | 107 | gulp.task('sprite-template', function () { 108 | return gulp.src(images, {encoding: false}) 109 | .pipe(spritesmith({ 110 | imgName: 'sprite.png', 111 | cssName: 'sprite.scss', 112 | cssTemplate: 'test-files/scss.template.handlebars', 113 | // Use `top-down` for easier testing 114 | algorithm: 'top-down' 115 | })) 116 | .pipe(gulp.dest('actual-files/template/', {encoding: false})); 117 | }); 118 | 119 | gulp.task('sprite-spritesheet-name', function () { 120 | return gulp.src(images, {encoding: false}) 121 | .pipe(spritesmith({ 122 | imgName: 'sprite.png', 123 | cssName: 'sprite.scss', 124 | cssSpritesheetName: 'icons', 125 | // Use `top-down` for easier testing 126 | algorithm: 'top-down' 127 | })) 128 | .pipe(gulp.dest('actual-files/spritesheet-name/', {encoding: false})); 129 | }); 130 | 131 | gulp.task('sprite-retina-mapped', function () { 132 | return gulp.src(retinaImages, {encoding: false}) 133 | .pipe(spritesmith({ 134 | retinaSrcFilter: 'test-files/*@2x.png', 135 | imgName: 'sprite.png', 136 | retinaImgName: 'sprite@2x.png', 137 | cssName: 'sprite.scss', 138 | cssSpritesheetName: 'icons', 139 | cssVarMap: function (sprite) { 140 | // Rename `sprite` to `icon` (e.g. `sprite1` -> `icon1`) 141 | sprite.name = sprite.name.replace('sprite', 'icon'); 142 | }, 143 | cssRetinaSpritesheetName: 'retina-icons', 144 | cssRetinaGroupsName: 'icon-groups', 145 | // Use `top-down` for easier testing 146 | algorithm: 'top-down' 147 | })) 148 | .pipe(gulp.dest('actual-files/retina-mapped/', {encoding: false})); 149 | }); 150 | 151 | gulp.task('sprite-empty', function () { 152 | return gulp.src(images, {encoding: false}) 153 | .pipe(through2.obj( 154 | // On data, do nothing and callback 155 | function onEmptyData(file, encoding, cb) { 156 | cb(); 157 | }, 158 | // On end, callback with nothing 159 | function onEmptyEnd(cb) { 160 | cb(); 161 | } 162 | )).pipe(spritesmith({ 163 | imgName: 'sprite.png', 164 | cssName: 'sprite.scss' 165 | })) 166 | .pipe(gulp.dest('actual-files/empty/', {encoding: false})); 167 | }); 168 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # gulp.spritesmith changelog 2 | 6.13.1 - Updated documentation and tests to support Gulp@5 3 | 4 | 6.13.0 - Upgraded to async@3.2.3 to fix GitHub vulnerability alert 5 | 6 | 6.12.1 - Updated Travis CI Node.js versions 7 | 8 | 6.12.0 - Upgraded to `underscore@1.13.1` to fix GitHub vulnerability warning. Fixes #155 9 | 10 | 6.11.0 - Upgraded to `spritesmith@3.4.0` to propagate `npm audit` fix 11 | 12 | 6.10.1 - Removed vulnerable js-yaml from dev dependencies 13 | 14 | 6.10.0 - Upgraded to spritesheet-templates@10.3.0 to remove LESS JS utilization 15 | 16 | 6.9.0 - Upgraded to `spritesmith@3.3.0` to fix Vinyl@2 and Gulp@4 support. Fixes #135 17 | 18 | 6.8.0 - Moved off of deprecated `gulp-util` 19 | 20 | 6.7.0 - Switched from `twolfson-style` to ESLint 21 | 22 | 6.6.0 - Removed support for Node.js < 4 23 | 24 | 6.5.1 - Documented cache busting support via `gulp-spritesmash` 25 | 26 | 6.5.0 - Added normal/retina name collision detection 27 | 28 | 6.4.0 - Upgraded all dependencies via @dankang in #116 29 | 30 | 6.3.0 - Upgraded to `spritesheet-templates@10.2.0` to move to appropriate SASS/SCSS commenting convention 31 | 32 | 6.2.1 - Upgraded to `spritesheet-templates@10.1.1` to correct `inline-block` examples in templates 33 | 34 | 6.2.0 - Upgraded to `spritesheet-templates@10.1.0` to add HTML comments to non-CSS templates 35 | 36 | 6.1.0 - Upgraded to `spritesmith@3.1.0` to add quality support for JPEGs 37 | 38 | 6.0.1 - Updated donation URL 39 | 40 | 6.0.0 - Upgraded to `spritesmith@3.0.0` and altered `img` contents from `Buffer` to stream 41 | 42 | 5.0.1 - Updated donation URL 43 | 44 | 5.0.0 - Upgraded to `spritesmith-engine-spec@2.0.0` 45 | 46 | 4.3.0 - Upgraded to `spritesmith@1.5.0` to add `specVersion` validation 47 | 48 | 4.2.4 - Updated link to specification 49 | 50 | 4.2.3 - Moved from Gratipay to bit.ly URL for donations 51 | 52 | 4.2.2 - Added newsletter badge to README 53 | 54 | 4.2.1 - Updated node versions to support `>= 0.10.0` 55 | 56 | 4.2.0 - Moved to emitting errors rather than assert to be more gulp-like. Fixes #73 57 | 58 | 4.1.2 - Added `foundry` for release 59 | 60 | 4.1.1 - Added clarification for `retinaSrcFilter` lining up with `gulp.src` 61 | 62 | 4.1.0 - Upgraded to `spritesmith@1.4.0` to add better PNG support 63 | 64 | 4.0.0 - Upgraded to `spritesheet-templates@10.0.0` and moved to @2x suffix to prevent ordering issues 65 | 66 | 3.8.2 - Added back iojs to Travis CI 67 | 68 | 3.8.1 - Added proper `end` handling to split streams in documentation and tests. Fixes #48 69 | 70 | 3.8.0 - Upgraded to `spritesheet-templates@9.6.0` to pick up `json_texture` template 71 | 72 | 3.7.2 - Moved iojs to allowed failure until https://github.com/npm/npm/issues/8406 gets patched 73 | 74 | 3.7.1 - Moved from deprecated `licenses` key to `license` in `package.json` 75 | 76 | 3.7.0 - Fixed retina size assertion logic via @radist2s in #47 77 | 78 | 3.6.1 - Added README touchups via Ensighten/grunt-spritesmith#131 79 | 80 | 3.6.0 - Upgraded to `spritesheet-templates@9.4.0` and added handlebars helper registration 81 | 82 | 3.5.4 - Added `node@0.12` and `iojs` to CI tests 83 | 84 | 3.5.3 - Upgraded to `url2@1.0.4` to fix `git` `node_modules` issues. Fixes #34 85 | 86 | 3.5.2 - Fixed missing output for retina image to `img` stream via @radist2s in #37 87 | 88 | 3.5.1 - Added documentation on retina template data 89 | 90 | 3.5.0 - Added retina spritesheet support 91 | 92 | 3.4.0 - Upgraded to `spritesheet-templates@9.3.1` to pick up `spritesheet_info` in anticipation for retina info 93 | 94 | 3.3.1 - Upgraded to `spritesheet-templates@9.2.2` to pick up more granular templating 95 | 96 | 3.3.0 - Upgraded to `spritesheet-templates@9.2.0` to add inheritance support 97 | 98 | 3.2.0 - Upgraded to `spritesmith@1.3.0` to pick up background fill support for `pixelsmith`. Fixes #33 99 | 100 | 3.1.0 - Upgraded to `spritesheet-templates@9.1.0` to add single sprite fixes/warnings 101 | 102 | 3.0.0 - Upgraded to `spritesheet-templates@9.0.0` to reintroduce `2.6.0` as a major release 103 | 104 | 2.8.0 - Upgraded to `spritesmith@1.2.0` to pick up optimal `binary-tree` fixes 105 | 106 | 2.7.0 - Reverted `2.6.0` to remove breaking changes. Fixes #30 107 | 108 | 2.6.0 - Upgraded to `spritesheet-templates@8.3.0` to pick up `variableNameTransforms` support 109 | 110 | 2.5.2 - Fixed broken test suite due to `spritesheet-templates` patch upgrade 111 | 112 | 2.5.1 - Fixed typo for `imgOpts` in README. Fixes #28 113 | 114 | 2.5.0 - Upgraded to `spritesmith@1.1.0` to pick up `binary-tree` algorithm changes 115 | 116 | 2.4.0 - Upgraded to `spritesheet-templates@8.2.0` to pick up preprocessor `spritesheet` variables and mixins 117 | 118 | 2.3.0 - Upgraded to `spritesheet-templates@8.0.0` to pick up `spritesheet` parameter 119 | 120 | 2.2.0 - Moved from `json2css` to `spritesheet-templates`, its renamed equivalent 121 | 122 | 2.1.0 - Upgraded to `json2css@6.1.0` to pick up CSS selector fix 123 | 124 | 2.0.1 - Added more examples and links to examples from other sections 125 | 126 | 2.0.0 - Major release with multiple breaking changes: 127 | 128 | - Upgraded to `spritesmith@1.0.0` 129 | - Moved to `pixelsmith` as default engine 130 | - Removed all other engines 131 | - Moved to `binary-tree` as default algorithm 132 | - Upgraded to `json2css@6.0.0` 133 | - Renames `cssClass` to `cssSelector` to make it more semantic 134 | 135 | 1.5.0 - Added `twolfson-style` and fixed up lint errors 136 | 137 | 1.4.1 - Updated `gittip` to `gratipay` 138 | 139 | 1.4.0 - Upgraded to `spritesmith@0.20.0` to pick up `phantomjssmith's` JPEG support 140 | 141 | 1.3.0 - Added support for `gulp-newer` by doing nothing when no images are provided. Fixes #17 142 | 143 | 1.2.0 - Moved return stream to `stream.Transform` via @elentok in #16 144 | 145 | 1.1.2 - Corrected example image for README 146 | 147 | 1.1.1 - Updated README 148 | 149 | 1.1.0 - Upgraded to `json2css@5.2.0` to pick up useful CSS comments 150 | 151 | 1.0.0 - Upgraded to `json2css@5.0.0` to collect `scss_maps` alteration 152 | 153 | 0.5.1 - Increased timeout for tests to prevent false negatives. Related to #6 154 | 155 | 0.5.0 - Upgraded to `spritesmith@0.19.0` and added `algorithmOpts` to support skipping image sorting 156 | 157 | 0.4.0 - Upgraded to `json2css@4.4.0` to pick up `scss_maps` height fix 158 | 159 | 0.3.0 - Upgraded to `json2css@4.3.0` to pick up `scss_maps` template 160 | 161 | 0.2.0 - Added support for `cssTemplate` via @backflip in #3 162 | 163 | 0.1.1 - Update all name references from `gulp-spritesmith` to `gulp.spritesmith` 164 | 165 | 0.1.0 - Initial release 166 | -------------------------------------------------------------------------------- /docs/examples/retina/sprite.styl: -------------------------------------------------------------------------------- 1 | /* 2 | Stylus variables are information about icon's compiled state, stored under its original file name 3 | 4 | .icon-home { 5 | width: $icon_home_width; 6 | } 7 | 8 | The large array-like variables contain all information about a single icon 9 | $icon_home = x y offset_x offset_y width height total_width total_height image_path; 10 | 11 | At the bottom of this section, we provide information about the spritesheet itself 12 | $spritesheet = width height image $spritesheet_sprites; 13 | */ 14 | $fork_name = 'fork'; 15 | $fork_x = 0px; 16 | $fork_y = 0px; 17 | $fork_offset_x = 0px; 18 | $fork_offset_y = 0px; 19 | $fork_width = 32px; 20 | $fork_height = 32px; 21 | $fork_total_width = 64px; 22 | $fork_total_height = 64px; 23 | $fork_image = 'sprite.png'; 24 | $fork = 0px 0px 0px 0px 32px 32px 64px 64px 'sprite.png' 'fork'; 25 | $github_name = 'github'; 26 | $github_x = 32px; 27 | $github_y = 0px; 28 | $github_offset_x = -32px; 29 | $github_offset_y = 0px; 30 | $github_width = 32px; 31 | $github_height = 32px; 32 | $github_total_width = 64px; 33 | $github_total_height = 64px; 34 | $github_image = 'sprite.png'; 35 | $github = 32px 0px -32px 0px 32px 32px 64px 64px 'sprite.png' 'github'; 36 | $twitter_name = 'twitter'; 37 | $twitter_x = 0px; 38 | $twitter_y = 32px; 39 | $twitter_offset_x = 0px; 40 | $twitter_offset_y = -32px; 41 | $twitter_width = 32px; 42 | $twitter_height = 32px; 43 | $twitter_total_width = 64px; 44 | $twitter_total_height = 64px; 45 | $twitter_image = 'sprite.png'; 46 | $twitter = 0px 32px 0px -32px 32px 32px 64px 64px 'sprite.png' 'twitter'; 47 | $fork_2x_name = 'fork@2x'; 48 | $fork_2x_x = 0px; 49 | $fork_2x_y = 0px; 50 | $fork_2x_offset_x = 0px; 51 | $fork_2x_offset_y = 0px; 52 | $fork_2x_width = 64px; 53 | $fork_2x_height = 64px; 54 | $fork_2x_total_width = 128px; 55 | $fork_2x_total_height = 128px; 56 | $fork_2x_image = 'sprite@2x.png'; 57 | $fork_2x = 0px 0px 0px 0px 64px 64px 128px 128px 'sprite@2x.png' 'fork@2x'; 58 | $github_2x_name = 'github@2x'; 59 | $github_2x_x = 64px; 60 | $github_2x_y = 0px; 61 | $github_2x_offset_x = -64px; 62 | $github_2x_offset_y = 0px; 63 | $github_2x_width = 64px; 64 | $github_2x_height = 64px; 65 | $github_2x_total_width = 128px; 66 | $github_2x_total_height = 128px; 67 | $github_2x_image = 'sprite@2x.png'; 68 | $github_2x = 64px 0px -64px 0px 64px 64px 128px 128px 'sprite@2x.png' 'github@2x'; 69 | $twitter_2x_name = 'twitter@2x'; 70 | $twitter_2x_x = 0px; 71 | $twitter_2x_y = 64px; 72 | $twitter_2x_offset_x = 0px; 73 | $twitter_2x_offset_y = -64px; 74 | $twitter_2x_width = 64px; 75 | $twitter_2x_height = 64px; 76 | $twitter_2x_total_width = 128px; 77 | $twitter_2x_total_height = 128px; 78 | $twitter_2x_image = 'sprite@2x.png'; 79 | $twitter_2x = 0px 64px 0px -64px 64px 64px 128px 128px 'sprite@2x.png' 'twitter@2x'; 80 | $spritesheet_width = 64px; 81 | $spritesheet_height = 64px; 82 | $spritesheet_image = 'sprite.png'; 83 | $spritesheet_sprites = $fork $github $twitter; 84 | $spritesheet = 64px 64px 'sprite.png' $spritesheet_sprites; 85 | $retina_spritesheet_width = 128px; 86 | $retina_spritesheet_height = 128px; 87 | $retina_spritesheet_image = 'sprite@2x.png'; 88 | $retina_spritesheet_sprites = $fork_2x $github_2x $twitter_2x; 89 | $retina_spritesheet = 128px 128px 'sprite@2x.png' $retina_spritesheet_sprites; 90 | 91 | /* 92 | These "retina group" variables are mappings for the naming and pairing of normal and retina sprites. 93 | 94 | The list formatted variables are intended for mixins like `retinaSprite` and `retinaSprites`. 95 | */ 96 | $fork_group_name = 'fork'; 97 | $fork_group = 'fork' $fork $fork_2x; 98 | $github_group_name = 'github'; 99 | $github_group = 'github' $github $github_2x; 100 | $twitter_group_name = 'twitter'; 101 | $twitter_group = 'twitter' $twitter $twitter_2x; 102 | $retina_groups = $fork_group $github_group $twitter_group; 103 | 104 | /* 105 | The provided mixins are intended to be used with the array-like variables 106 | 107 | .icon-home { 108 | spriteWidth($icon_home) 109 | } 110 | 111 | .icon-email { 112 | sprite($icon_email) 113 | } 114 | 115 | Example usage in HTML: 116 | 117 | `display: block` sprite: 118 |
119 | 120 | To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 121 | 122 | // CSS 123 | .icon { 124 | display: inline-block; 125 | } 126 | 127 | // HTML 128 | 129 | */ 130 | spriteWidth($sprite) { 131 | width: $sprite[4]; 132 | } 133 | 134 | spriteHeight($sprite) { 135 | height: $sprite[5]; 136 | } 137 | 138 | spritePosition($sprite) { 139 | background-position: $sprite[2] $sprite[3]; 140 | } 141 | 142 | spriteImage($sprite) { 143 | background-image: url($sprite[8]); 144 | } 145 | 146 | sprite($sprite) { 147 | spriteImage($sprite) 148 | spritePosition($sprite) 149 | spriteWidth($sprite) 150 | spriteHeight($sprite) 151 | } 152 | 153 | /* 154 | The `retinaSprite` mixin sets up rules and a media query for a sprite/retina sprite. 155 | It should be used with a "retina group" variable. 156 | 157 | The media query is from CSS Tricks: https://css-tricks.com/snippets/css/retina-display-media-query/ 158 | 159 | $icon_home_group = 'icon-home' $icon_home $icon_home_2x; 160 | 161 | .icon-home { 162 | retinaSprite($icon_home_group) 163 | } 164 | */ 165 | spriteBackgroundSize($sprite) { 166 | background-size: $sprite[6] $sprite[7]; 167 | } 168 | 169 | retinaSprite($retina_group) { 170 | $normal_sprite = $retina_group[1]; 171 | $retina_sprite = $retina_group[2]; 172 | sprite($normal_sprite) 173 | 174 | @media (-webkit-min-device-pixel-ratio: 2), 175 | (min-resolution: 192dpi) { 176 | spriteImage($retina_sprite) 177 | spriteBackgroundSize($normal_sprite) 178 | } 179 | } 180 | 181 | /* 182 | The `sprites` mixin generates identical output to the CSS template 183 | but can be overridden inside of Stylus 184 | 185 | This must be run when you have at least 2 sprites. 186 | If run with a single sprite, then there will be reference errors. 187 | 188 | sprites($spritesheet_sprites); 189 | */ 190 | sprites($sprites) { 191 | for $sprite in $sprites { 192 | $sprite_name = $sprite[9]; 193 | .{$sprite_name} { 194 | sprite($sprite); 195 | } 196 | } 197 | } 198 | 199 | /* 200 | The `retinaSprites` mixin generates a CSS rule and media query for retina groups 201 | This yields the same output as CSS retina template but can be overridden in Stylus 202 | 203 | retinaSprites($retina_groups); 204 | */ 205 | retinaSprites($retina_groups) { 206 | for $retina_group in $retina_groups { 207 | $sprite_name = $retina_group[0]; 208 | .{$sprite_name} { 209 | retinaSprite($retina_group); 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /test/expected-files/retina-mapped/sprite.scss: -------------------------------------------------------------------------------- 1 | // SCSS variables are information about icon's compiled state, stored under its original file name 2 | // 3 | // .icon-home { 4 | // width: $icon-home-width; 5 | // } 6 | // 7 | // The large array-like variables contain all information about a single icon 8 | // $icon-home: x y offset_x offset_y width height total_width total_height image_path; 9 | // 10 | // At the bottom of this section, we provide information about the spritesheet itself 11 | // $spritesheet: width height image $spritesheet-sprites; 12 | $icon1-name: 'icon1'; 13 | $icon1-x: 0px; 14 | $icon1-y: 0px; 15 | $icon1-offset-x: 0px; 16 | $icon1-offset-y: 0px; 17 | $icon1-width: 50px; 18 | $icon1-height: 50px; 19 | $icon1-total-width: 100px; 20 | $icon1-total-height: 300px; 21 | $icon1-image: 'sprite.png'; 22 | $icon1: (0px, 0px, 0px, 0px, 50px, 50px, 100px, 300px, 'sprite.png', 'icon1', ); 23 | $icon2-name: 'icon2'; 24 | $icon2-x: 0px; 25 | $icon2-y: 50px; 26 | $icon2-offset-x: 0px; 27 | $icon2-offset-y: -50px; 28 | $icon2-width: 50px; 29 | $icon2-height: 50px; 30 | $icon2-total-width: 100px; 31 | $icon2-total-height: 300px; 32 | $icon2-image: 'sprite.png'; 33 | $icon2: (0px, 50px, 0px, -50px, 50px, 50px, 100px, 300px, 'sprite.png', 'icon2', ); 34 | $icon3-name: 'icon3'; 35 | $icon3-x: 0px; 36 | $icon3-y: 100px; 37 | $icon3-offset-x: 0px; 38 | $icon3-offset-y: -100px; 39 | $icon3-width: 100px; 40 | $icon3-height: 200px; 41 | $icon3-total-width: 100px; 42 | $icon3-total-height: 300px; 43 | $icon3-image: 'sprite.png'; 44 | $icon3: (0px, 100px, 0px, -100px, 100px, 200px, 100px, 300px, 'sprite.png', 'icon3', ); 45 | $icon1-2x-name: 'icon1@2x'; 46 | $icon1-2x-x: 0px; 47 | $icon1-2x-y: 0px; 48 | $icon1-2x-offset-x: 0px; 49 | $icon1-2x-offset-y: 0px; 50 | $icon1-2x-width: 100px; 51 | $icon1-2x-height: 100px; 52 | $icon1-2x-total-width: 200px; 53 | $icon1-2x-total-height: 600px; 54 | $icon1-2x-image: 'sprite@2x.png'; 55 | $icon1-2x: (0px, 0px, 0px, 0px, 100px, 100px, 200px, 600px, 'sprite@2x.png', 'icon1@2x', ); 56 | $icon2-2x-name: 'icon2@2x'; 57 | $icon2-2x-x: 0px; 58 | $icon2-2x-y: 100px; 59 | $icon2-2x-offset-x: 0px; 60 | $icon2-2x-offset-y: -100px; 61 | $icon2-2x-width: 100px; 62 | $icon2-2x-height: 100px; 63 | $icon2-2x-total-width: 200px; 64 | $icon2-2x-total-height: 600px; 65 | $icon2-2x-image: 'sprite@2x.png'; 66 | $icon2-2x: (0px, 100px, 0px, -100px, 100px, 100px, 200px, 600px, 'sprite@2x.png', 'icon2@2x', ); 67 | $icon3-2x-name: 'icon3@2x'; 68 | $icon3-2x-x: 0px; 69 | $icon3-2x-y: 200px; 70 | $icon3-2x-offset-x: 0px; 71 | $icon3-2x-offset-y: -200px; 72 | $icon3-2x-width: 200px; 73 | $icon3-2x-height: 400px; 74 | $icon3-2x-total-width: 200px; 75 | $icon3-2x-total-height: 600px; 76 | $icon3-2x-image: 'sprite@2x.png'; 77 | $icon3-2x: (0px, 200px, 0px, -200px, 200px, 400px, 200px, 600px, 'sprite@2x.png', 'icon3@2x', ); 78 | $icons-width: 100px; 79 | $icons-height: 300px; 80 | $icons-image: 'sprite.png'; 81 | $icons-sprites: ($icon1, $icon2, $icon3, ); 82 | $icons: (100px, 300px, 'sprite.png', $icons-sprites, ); 83 | $retina-icons-width: 200px; 84 | $retina-icons-height: 600px; 85 | $retina-icons-image: 'sprite@2x.png'; 86 | $retina-icons-sprites: ($icon1-2x, $icon2-2x, $icon3-2x, ); 87 | $retina-icons: (200px, 600px, 'sprite@2x.png', $retina-icons-sprites, ); 88 | 89 | // These "retina group" variables are mappings for the naming and pairing of normal and retina sprites. 90 | // 91 | // The list formatted variables are intended for mixins like `retina-sprite` and `retina-sprites`. 92 | $icon1-group-name: 'icon1'; 93 | $icon1-group: ('icon1', $icon1, $icon1-2x, ); 94 | $icon2-group-name: 'icon2'; 95 | $icon2-group: ('icon2', $icon2, $icon2-2x, ); 96 | $icon3-group-name: 'icon3'; 97 | $icon3-group: ('icon3', $icon3, $icon3-2x, ); 98 | $icon-groups: ($icon1-group, $icon2-group, $icon3-group, ); 99 | 100 | // The provided mixins are intended to be used with the array-like variables 101 | // 102 | // .icon-home { 103 | // @include sprite-width($icon-home); 104 | // } 105 | // 106 | // .icon-email { 107 | // @include sprite($icon-email); 108 | // } 109 | // 110 | // Example usage in HTML: 111 | // 112 | // `display: block` sprite: 113 | //
114 | // 115 | // To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: 116 | // 117 | // // CSS 118 | // .icon { 119 | // display: inline-block; 120 | // } 121 | // 122 | // // HTML 123 | // 124 | @mixin sprite-width($sprite) { 125 | width: nth($sprite, 5); 126 | } 127 | 128 | @mixin sprite-height($sprite) { 129 | height: nth($sprite, 6); 130 | } 131 | 132 | @mixin sprite-position($sprite) { 133 | $sprite-offset-x: nth($sprite, 3); 134 | $sprite-offset-y: nth($sprite, 4); 135 | background-position: $sprite-offset-x $sprite-offset-y; 136 | } 137 | 138 | @mixin sprite-image($sprite) { 139 | $sprite-image: nth($sprite, 9); 140 | background-image: url(#{$sprite-image}); 141 | } 142 | 143 | @mixin sprite($sprite) { 144 | @include sprite-image($sprite); 145 | @include sprite-position($sprite); 146 | @include sprite-width($sprite); 147 | @include sprite-height($sprite); 148 | } 149 | 150 | // The `retina-sprite` mixin sets up rules and a media query for a sprite/retina sprite. 151 | // It should be used with a "retina group" variable. 152 | // 153 | // The media query is from CSS Tricks: https://css-tricks.com/snippets/css/retina-display-media-query/ 154 | // 155 | // $icon-home-group: ('icon-home', $icon-home, $icon-home-2x, ); 156 | // 157 | // .icon-home { 158 | // @include retina-sprite($icon-home-group); 159 | // } 160 | @mixin sprite-background-size($sprite) { 161 | $sprite-total-width: nth($sprite, 7); 162 | $sprite-total-height: nth($sprite, 8); 163 | background-size: $sprite-total-width $sprite-total-height; 164 | } 165 | 166 | @mixin retina-sprite($retina-group) { 167 | $normal-sprite: nth($retina-group, 2); 168 | $retina-sprite: nth($retina-group, 3); 169 | @include sprite($normal-sprite); 170 | 171 | @media (-webkit-min-device-pixel-ratio: 2), 172 | (min-resolution: 192dpi) { 173 | @include sprite-image($retina-sprite); 174 | @include sprite-background-size($normal-sprite); 175 | } 176 | } 177 | 178 | // The `sprites` mixin generates identical output to the CSS template 179 | // but can be overridden inside of SCSS 180 | // 181 | // @include sprites($spritesheet-sprites); 182 | @mixin sprites($sprites) { 183 | @each $sprite in $sprites { 184 | $sprite-name: nth($sprite, 10); 185 | .#{$sprite-name} { 186 | @include sprite($sprite); 187 | } 188 | } 189 | } 190 | 191 | // The `retina-sprites` mixin generates a CSS rule and media query for retina groups 192 | // This yields the same output as CSS retina template but can be overridden in SCSS 193 | // 194 | // @include retina-sprites($retina-groups); 195 | @mixin retina-sprites($retina-groups) { 196 | @each $retina-group in $retina-groups { 197 | $sprite-name: nth($retina-group, 1); 198 | .#{$sprite-name} { 199 | @include retina-sprite($retina-group); 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /test/gulp-spritesmith_test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var fs = require('fs'); 3 | var rimraf = require('rimraf'); 4 | var imageUtils = require('./utils/image.js'); 5 | var childUtils = require('./utils/child.js'); 6 | 7 | // Clean up actual-files directory 8 | before(function (done) { 9 | rimraf(__dirname + '/actual-files/', done); 10 | }); 11 | 12 | describe('gulp.spritesmith', function () { 13 | describe('running a task without any options', function () { 14 | childUtils.run('gulp sprite-default'); 15 | imageUtils.loadActual(__dirname + '/actual-files/default/sprite.png'); 16 | imageUtils.loadExpected(__dirname + '/expected-files/default/pixelsmith.png'); 17 | 18 | it('generates an image', function () { 19 | assert.deepEqual(this.actualPixels, this.expectedPixels); 20 | }); 21 | 22 | it('generates a css file', function () { 23 | var actualCss = fs.readFileSync(__dirname + '/actual-files/default/sprite.css', 'utf8'); 24 | var expectedCss = fs.readFileSync(__dirname + '/expected-files/default/sprite.css', 'utf8'); 25 | assert.strictEqual(actualCss, expectedCss); 26 | }); 27 | }); 28 | 29 | describe('running a task with retina options', function () { 30 | childUtils.run('gulp sprite-retina'); 31 | imageUtils.loadActual(__dirname + '/actual-files/retina/sprite.png'); 32 | imageUtils.loadExpected(__dirname + '/expected-files/retina/pixelsmith.png'); 33 | 34 | it('generates an image', function () { 35 | assert.deepEqual(this.actualPixels, this.expectedPixels); 36 | }); 37 | 38 | describe('with respect to the retina image', function () { 39 | imageUtils.loadActual(__dirname + '/actual-files/retina/sprite@2x.png'); 40 | imageUtils.loadExpected(__dirname + '/expected-files/retina/pixelsmith@2x.png'); 41 | 42 | it('generates an image', function () { 43 | assert.deepEqual(this.actualPixels, this.expectedPixels); 44 | }); 45 | }); 46 | 47 | it('generates a css file', function () { 48 | var actualCss = fs.readFileSync(__dirname + '/actual-files/retina/sprite.css', 'utf8'); 49 | var expectedCss = fs.readFileSync(__dirname + '/expected-files/retina/sprite.css', 'utf8'); 50 | assert.strictEqual(actualCss, expectedCss); 51 | }); 52 | }); 53 | 54 | describe('returns "img" and "css" streams', function () { 55 | childUtils.run('gulp sprite-two-streams'); 56 | imageUtils.loadActual(__dirname + '/actual-files/two-streams/sprite.png'); 57 | imageUtils.loadExpected(__dirname + '/expected-files/two-streams/pixelsmith.png'); 58 | 59 | it('generates an image', function () { 60 | assert.deepEqual(this.actualPixels, this.expectedPixels); 61 | }); 62 | 63 | it('generates a css file', function () { 64 | var actualCss = fs.readFileSync(__dirname + '/actual-files/two-streams/sprite.css', 'utf8'); 65 | var expectedCss = fs.readFileSync(__dirname + '/expected-files/two-streams/sprite.css', 'utf8'); 66 | assert.strictEqual(actualCss, expectedCss); 67 | }); 68 | }); 69 | 70 | describe('returns retina "img" in two streams', function () { 71 | childUtils.run('gulp sprite-retina-two-streams'); 72 | imageUtils.loadActual(__dirname + '/actual-files/retina-two-streams/sprite.png'); 73 | imageUtils.loadExpected(__dirname + '/expected-files/retina-two-streams/pixelsmith.png'); 74 | 75 | it('generates an image', function () { 76 | assert.deepEqual(this.actualPixels, this.expectedPixels); 77 | }); 78 | 79 | describe('with respect to the retina image', function () { 80 | imageUtils.loadActual(__dirname + '/actual-files/retina-two-streams/sprite@2x.png'); 81 | imageUtils.loadExpected(__dirname + '/expected-files/retina-two-streams/pixelsmith@2x.png'); 82 | 83 | it('generates an image', function () { 84 | assert.deepEqual(this.actualPixels, this.expectedPixels); 85 | }); 86 | }); 87 | }); 88 | 89 | describe('running a task where a normal and retina image have same name', function () { 90 | childUtils.runSaveError('gulp sprite-retina-same-name'); 91 | 92 | it('outputs an error', function () { 93 | assert.notEqual(this.err, null); 94 | assert.notEqual( 95 | this.err.message.indexOf('Normal and retina sprites have same names: ["sprite1","sprite2","sprite3"]'), 96 | -1); 97 | }); 98 | }); 99 | 100 | describe('running a task with output formats', function () { 101 | childUtils.run('gulp sprite-formats'); 102 | imageUtils.loadActual(__dirname + '/actual-files/formats/sprite.jpg'); 103 | imageUtils.loadExpected(__dirname + '/expected-files/formats/mint-pngsmith.png'); 104 | 105 | it('generates a top-down png (as a .jpg)', function () { 106 | assert.deepEqual(this.actualPixels, this.expectedPixels); 107 | }); 108 | 109 | it('generates a Stylus file (as a .css)', function () { 110 | var actualCss = fs.readFileSync(__dirname + '/actual-files/formats/sprite.css', 'utf8'); 111 | var expectedCss = fs.readFileSync(__dirname + '/expected-files/formats/sprite.styl', 'utf8'); 112 | assert.strictEqual(actualCss, expectedCss); 113 | }); 114 | }); 115 | 116 | describe('running a task with engine and algorithm options', function () { 117 | childUtils.run('gulp sprite-options'); 118 | imageUtils.loadActual(__dirname + '/actual-files/options/sprite.png'); 119 | imageUtils.loadExpected(__dirname + '/expected-files/options/mint-pngsmith.png'); 120 | 121 | it('generates an alt-diagonal png via the gm engine', function () { 122 | assert.deepEqual(this.actualPixels, this.expectedPixels); 123 | }); 124 | 125 | it('generates a CSS file with alt-diagonal coordinates', function () { 126 | var actualCss = fs.readFileSync(__dirname + '/actual-files/options/sprite.css', 'utf8'); 127 | var expectedCss = fs.readFileSync(__dirname + '/expected-files/options/sprite.css', 'utf8'); 128 | assert.strictEqual(actualCss, expectedCss); 129 | }); 130 | }); 131 | 132 | describe('running a task with a custom CSS template', function () { 133 | childUtils.run('gulp sprite-template'); 134 | imageUtils.loadActual(__dirname + '/actual-files/template/sprite.png'); 135 | imageUtils.loadExpected(__dirname + '/expected-files/template/mint-graphicsmagick.png'); 136 | 137 | it('generates a top-down png', function () { 138 | assert.deepEqual(this.actualPixels, this.expectedPixels); 139 | }); 140 | 141 | it('generates a css file', function () { 142 | var actualCss = fs.readFileSync(__dirname + '/actual-files/template/sprite.scss', 'utf8'); 143 | var expectedCss = fs.readFileSync(__dirname + '/expected-files/template/sprite.scss', 'utf8'); 144 | assert.strictEqual(actualCss, expectedCss); 145 | }); 146 | }); 147 | 148 | describe('running a task with a custom spritesheet prefix', function () { 149 | childUtils.run('gulp sprite-spritesheet-name'); 150 | 151 | it('generates a css file', function () { 152 | var actualCss = fs.readFileSync(__dirname + '/actual-files/spritesheet-name/sprite.scss', 'utf8'); 153 | var expectedCss = fs.readFileSync(__dirname + '/expected-files/spritesheet-name/sprite.scss', 'utf8'); 154 | assert.strictEqual(actualCss, expectedCss); 155 | }); 156 | }); 157 | 158 | describe('running a task with mapped retina options', function () { 159 | childUtils.run('gulp sprite-retina-mapped'); 160 | 161 | it('generates a mapped css file', function () { 162 | var actualCss = fs.readFileSync(__dirname + '/actual-files/retina-mapped/sprite.scss', 'utf8'); 163 | var expectedCss = fs.readFileSync(__dirname + '/expected-files/retina-mapped/sprite.scss', 'utf8'); 164 | assert.strictEqual(actualCss, expectedCss); 165 | }); 166 | }); 167 | 168 | // DEV: `gulp-newer` presents no input files when the task does not need to be run. See #17 169 | describe('running a task with no input images', function () { 170 | childUtils.run('gulp sprite-empty'); 171 | 172 | it('does not generate a top-down png', function () { 173 | var imgExists = fs.existsSync(__dirname + '/actual-files/empty/sprite.png'); 174 | assert.strictEqual(imgExists, false); 175 | }); 176 | 177 | it('does not generate a css file', function () { 178 | var cssExists = fs.existsSync(__dirname + '/actual-files/empty/sprite.scss'); 179 | assert.strictEqual(cssExists, false); 180 | }); 181 | }); 182 | }); 183 | -------------------------------------------------------------------------------- /lib/gulp-spritesmith.js: -------------------------------------------------------------------------------- 1 | // Load our dependencies 2 | var assert = require('assert'); 3 | var async = require('async'); 4 | var fs = require('fs'); 5 | var path = require('path'); 6 | var _ = require('underscore'); 7 | var Vinyl = require('vinyl'); 8 | var Minimatch = require('minimatch').Minimatch; 9 | var templater = require('spritesheet-templates'); 10 | var Spritesmith = require('spritesmith'); 11 | var through2 = require('through2'); 12 | var url = require('url2'); 13 | 14 | function ExtFormat() { 15 | this.formatObj = {}; 16 | } 17 | ExtFormat.prototype = { 18 | add: function (name, val) { 19 | this.formatObj[name] = val; 20 | }, 21 | get: function (filepath) { 22 | // Grab the extension from the filepath 23 | var ext = path.extname(filepath); 24 | var lowerExt = ext.toLowerCase(); 25 | 26 | // Look up the file extenion from our format object 27 | var formatObj = this.formatObj; 28 | var format = formatObj[lowerExt]; 29 | return format; 30 | } 31 | }; 32 | 33 | // Create img and css formats 34 | var imgFormats = new ExtFormat(); 35 | var cssFormats = new ExtFormat(); 36 | 37 | // Add our img formats 38 | imgFormats.add('.png', 'png'); 39 | imgFormats.add('.jpg', 'jpeg'); 40 | imgFormats.add('.jpeg', 'jpeg'); 41 | 42 | // Add our css formats 43 | cssFormats.add('.styl', 'stylus'); 44 | cssFormats.add('.stylus', 'stylus'); 45 | cssFormats.add('.sass', 'sass'); 46 | cssFormats.add('.scss', 'scss'); 47 | cssFormats.add('.less', 'less'); 48 | cssFormats.add('.json', 'json'); 49 | cssFormats.add('.css', 'css'); 50 | 51 | // Copy/paste helper from gulp 52 | // https://github.com/wearefractal/glob-stream/blob/v5.0.0/index.js#L131-L138 53 | function unrelative(cwd, glob) { 54 | var mod = ''; 55 | if (glob[0] === '!') { 56 | mod = glob[0]; 57 | glob = glob.slice(1); 58 | } 59 | return mod + path.resolve(cwd, glob); 60 | } 61 | 62 | // Define helper for coordinate naming 63 | function getCoordinateName(filepath) { 64 | // Extract the image name (exlcuding extension) 65 | var fullname = path.basename(filepath); 66 | var nameParts = fullname.split('.'); 67 | 68 | // If there is are more than 2 parts, pop the last one 69 | if (nameParts.length >= 2) { 70 | nameParts.pop(); 71 | } 72 | 73 | // Return our modified filename 74 | return nameParts.join('.'); 75 | } 76 | 77 | // Create a gulp-spritesmith function 78 | function gulpSpritesmith(params) { 79 | var imgName = params.imgName; 80 | var cssName = params.cssName; 81 | assert(imgName, 'An `imgName` parameter was not provided to `gulp.spritesmith` (required)'); 82 | assert(cssName, 'A `cssName` parameter was not provided to `gulp.spritesmith` (required)'); 83 | 84 | // If there are settings for retina, verify our all of them are present 85 | var retinaSrcFilter = params.retinaSrcFilter; 86 | var retinaImgName = params.retinaImgName; 87 | if (retinaSrcFilter || retinaImgName) { 88 | assert(retinaSrcFilter && retinaImgName, 'Retina settings detected. We must have both `retinaSrcFilter` and ' + 89 | '`retinaImgName` provided for retina to work'); 90 | } 91 | 92 | // Define our output streams 93 | var imgStream = through2.obj(); 94 | var cssStream = through2.obj(); 95 | 96 | // Create a stream to take in images 97 | var images = []; 98 | var onData = function (file, encoding, cb) { 99 | images.push(file); 100 | cb(); 101 | }; 102 | 103 | // When we have completed our input 104 | var onEnd = function (cb) { 105 | // If there are no images present, exit early 106 | // DEV: This is against the behavior of `spritesmith` but pro-gulp 107 | // DEV: See https://github.com/twolfson/gulp.spritesmith/issues/17 108 | if (images.length === 0) { 109 | imgStream.push(null); 110 | cssStream.push(null); 111 | return cb(); 112 | } 113 | 114 | // Determine the format of the image 115 | var imgOpts = params.imgOpts || {}; 116 | var imgFormat = imgOpts.format || imgFormats.get(imgName) || 'png'; 117 | 118 | // Set up the defautls for imgOpts 119 | imgOpts = _.defaults({}, imgOpts, {format: imgFormat}); 120 | 121 | // If we have retina settings, filter out the retina images 122 | var retinaImages; 123 | if (retinaSrcFilter) { 124 | // Filter out our retina files 125 | // https://github.com/wearefractal/glob-stream/blob/v5.0.0/index.js#L84-L87 126 | retinaImages = []; 127 | var retinaSrcPatterns = Array.isArray(retinaSrcFilter) ? retinaSrcFilter : [retinaSrcFilter]; 128 | images = images.filter(function filterSrcFile(file) { 129 | // If we have a retina file, filter it out 130 | var matched = retinaSrcPatterns.some(function matchMinimatches(retinaSrcPattern) { 131 | var minimatch = new Minimatch(unrelative(file.cwd, retinaSrcPattern)); 132 | return minimatch.match(file.path); 133 | }); 134 | if (matched) { 135 | retinaImages.push(file); 136 | return false; 137 | // Otherwise, keep it in the src files 138 | } else { 139 | return true; 140 | } 141 | }); 142 | 143 | // If we have a different amount of normal and retina images, complain and leave 144 | if (images.length !== retinaImages.length) { 145 | var err = new Error('Retina settings detected but ' + retinaImages.length + ' retina images were found. ' + 146 | 'We have ' + images.length + ' normal images and expect these numbers to line up. ' + 147 | 'Please double check `retinaSrcFilter`.'); 148 | err.images = images; 149 | err.retinaImages = retinaImages; 150 | this.emit('error', err); 151 | imgStream.push(null); 152 | cssStream.push(null); 153 | return cb(); 154 | } 155 | } 156 | 157 | // Prepare spritesmith parameters 158 | var spritesmithParams = { 159 | engine: params.engine, 160 | algorithm: params.algorithm, 161 | padding: params.padding || 0, 162 | algorithmOpts: params.algorithmOpts || {}, 163 | engineOpts: params.engineOpts || {}, 164 | exportOpts: imgOpts 165 | }; 166 | var that = this; 167 | 168 | // Construct our spritesmiths 169 | var spritesmith = new Spritesmith(spritesmithParams); 170 | var retinaSpritesmithParams; // eslint-disable-line 171 | var retinaSpritesmith; // eslint-disable-line 172 | if (retinaImages) { 173 | retinaSpritesmithParams = _.defaults({ 174 | padding: spritesmithParams.padding * 2 175 | }, spritesmithParams); 176 | retinaSpritesmith = new Spritesmith(retinaSpritesmithParams); 177 | } 178 | 179 | // In parallel 180 | async.parallel([ 181 | // Load in our normal images 182 | function generateNormalImages(callback) { 183 | spritesmith.createImages(images, callback); 184 | }, 185 | // If we have retina images, load them in as well 186 | function generateRetinaSpritesheet(callback) { 187 | if (retinaImages) { 188 | return retinaSpritesmith.createImages(retinaImages, callback); 189 | } else { 190 | return process.nextTick(callback); 191 | } 192 | } 193 | ], function handleImages(err, resultArr) { 194 | // If an error occurred, emit it 195 | if (err) { 196 | return cb(err); 197 | } 198 | 199 | // Otherwise, validate our images line up 200 | var normalSprites = resultArr[0]; 201 | var retinaSprites = resultArr[1]; 202 | 203 | // If we have retina images, verify the widths line up 204 | if (retinaSprites) { 205 | // Perform our assertions 206 | var errorEncountered = false; 207 | normalSprites.forEach(function validateImageSizes(normalSprite, i) { 208 | var retinaSprite = retinaSprites[i]; 209 | if (retinaSprite.width !== normalSprite.width * 2 || retinaSprite.height !== normalSprite.height * 2) { 210 | errorEncountered = true; 211 | var err = new Error('Normal sprite has inconsistent size with retina sprite. ' + 212 | '"' + images[i].path + '" is ' + normalSprite.width + 'x' + normalSprite.height + ' while ' + 213 | '"' + retinaImages[i].path + '" is ' + retinaSprite.width + 'x' + retinaSprite.height + '.'); 214 | err.normalSprite = normalSprite; 215 | err.retinaSprite = retinaSprite; 216 | that.emit('error', err); 217 | } 218 | }); 219 | 220 | // If there was an error, then bail out 221 | if (errorEncountered) { 222 | imgStream.push(null); 223 | cssStream.push(null); 224 | return cb(); 225 | } 226 | } 227 | 228 | // Process our images now 229 | var result = spritesmith.processImages(normalSprites, spritesmithParams); 230 | var retinaResult; 231 | if (retinaSprites) { 232 | retinaResult = retinaSpritesmith.processImages(retinaSprites, retinaSpritesmithParams); 233 | } 234 | 235 | // START OF DUPLICATE CODE FROM grunt-spritesmith 236 | // Generate a listing of CSS variables 237 | var coordinates = result.coordinates; 238 | var properties = result.properties; 239 | var spritePath = params.imgPath || url.relative(cssName, imgName); 240 | var spritesheetData = { 241 | width: properties.width, 242 | height: properties.height, 243 | image: spritePath 244 | }; 245 | var cssVarMap = params.cssVarMap || function noop() {}; 246 | var cleanCoords = []; 247 | 248 | // Clean up the file name of the file 249 | Object.getOwnPropertyNames(coordinates).sort().forEach(function (file) { 250 | // Extract out our name 251 | var name = getCoordinateName(file); 252 | var coords = coordinates[file]; 253 | 254 | // Specify the image for the sprite 255 | coords.name = name; 256 | coords.source_image = file; 257 | // DEV: `image`, `total_width`, `total_height` are deprecated as they are overwritten in `spritesheet-templates` 258 | coords.image = spritePath; 259 | coords.total_width = properties.width; 260 | coords.total_height = properties.height; 261 | 262 | // Map the coordinates through cssVarMap 263 | coords = cssVarMap(coords) || coords; 264 | 265 | // Save the cleaned name and coordinates 266 | cleanCoords.push(coords); 267 | }); 268 | 269 | // If we have retina sprites 270 | var retinaCleanCoords; // eslint-disable-line 271 | var retinaGroups; // eslint-disable-line 272 | var retinaSpritesheetInfo; // eslint-disable-line 273 | if (retinaResult) { 274 | // Generate a listing of CSS variables 275 | var retinaCoordinates = retinaResult.coordinates; 276 | var retinaProperties = retinaResult.properties; 277 | var retinaSpritePath = params.retinaImgPath || url.relative(cssName, retinaImgName); 278 | retinaSpritesheetInfo = { 279 | width: retinaProperties.width, 280 | height: retinaProperties.height, 281 | image: retinaSpritePath 282 | }; 283 | // DEV: We reuse cssVarMap 284 | retinaCleanCoords = []; 285 | 286 | // Clean up the file name of the file 287 | Object.getOwnPropertyNames(retinaCoordinates).sort().forEach(function prepareRetinaTemplateData(file) { 288 | var name = getCoordinateName(file); 289 | var coords = retinaCoordinates[file]; 290 | coords.name = name; 291 | coords.source_image = file; 292 | coords.image = retinaSpritePath; 293 | coords.total_width = retinaProperties.width; 294 | coords.total_height = retinaProperties.height; 295 | coords = cssVarMap(coords) || coords; 296 | retinaCleanCoords.push(coords); 297 | }); 298 | 299 | // Verify we have no conflicting file names (e.g. `1x/home.png` and `2x/home.png`) 300 | // https://github.com/twolfson/gulp.spritesmith/issues/124 301 | var cleanCoordNames = _.pluck(cleanCoords, 'name'); 302 | var retinaCleanCoordNames = _.pluck(retinaCleanCoords, 'name'); 303 | var intersectingNames = _.intersection(cleanCoordNames, retinaCleanCoordNames); 304 | if (intersectingNames.length) { 305 | throw new Error('Normal and retina sprites have same names: ' + JSON.stringify(intersectingNames) + '. ' + 306 | 'Please rename them to different names (e.g. `-1x`, `-2x`) or use `cssVarMap` to prevent collisions. ' + 307 | 'See https://github.com/twolfson/gulp.spritesmith/issues/124 for more info'); 308 | } 309 | 310 | // Generate groups for our coordinates 311 | retinaGroups = cleanCoords.map(function getRetinaGroups(normalSprite, i) { 312 | // Generate our group 313 | // DEV: Name is inherited from `cssVarMap` on normal sprite 314 | return { 315 | name: normalSprite.name, 316 | index: i 317 | }; 318 | }); 319 | } 320 | 321 | // If we have handlebars helpers, register them 322 | var handlebarsHelpers = params.cssHandlebarsHelpers; 323 | if (handlebarsHelpers) { 324 | Object.keys(handlebarsHelpers).forEach(function registerHelper(helperKey) { 325 | templater.registerHandlebarsHelper(helperKey, handlebarsHelpers[helperKey]); 326 | }); 327 | } 328 | 329 | // If there is a custom template, use it 330 | var cssFormat = 'spritesmith-custom'; 331 | var cssTemplate = params.cssTemplate; 332 | if (cssTemplate) { 333 | if (typeof cssTemplate === 'function') { 334 | templater.addTemplate(cssFormat, cssTemplate); 335 | } else { 336 | templater.addHandlebarsTemplate(cssFormat, fs.readFileSync(cssTemplate, 'utf8')); 337 | } 338 | // Otherwise, override the cssFormat and fallback to 'json' 339 | } else { 340 | cssFormat = params.cssFormat; 341 | if (!cssFormat) { 342 | cssFormat = cssFormats.get(cssName) || 'json'; 343 | 344 | // If we are dealing with retina items, move to retina flavor (e.g. `scss` -> `scss_retina`) 345 | if (retinaGroups) { 346 | cssFormat += '_retina'; 347 | } 348 | } 349 | } 350 | 351 | // Render the variables via `spritesheet-templates` 352 | var cssStr = templater({ 353 | sprites: cleanCoords, 354 | spritesheet: spritesheetData, 355 | spritesheet_info: { 356 | name: params.cssSpritesheetName 357 | }, 358 | retina_groups: retinaGroups, 359 | retina_sprites: retinaCleanCoords, 360 | retina_spritesheet: retinaSpritesheetInfo, 361 | retina_spritesheet_info: { 362 | name: params.cssRetinaSpritesheetName 363 | }, 364 | retina_groups_info: { 365 | name: params.cssRetinaGroupsName 366 | } 367 | }, { 368 | format: cssFormat, 369 | formatOpts: params.cssOpts || {} 370 | }); 371 | // END OF DUPLICATE CODE FROM grunt-spritesmith 372 | 373 | // Pipe out images as streams and forward their errors 374 | // TODO: Consider making joint stream default 375 | // but allow for split stream which has more distinct errors 376 | // e.g. spritesmith.split() = {css, img} 377 | result.image.on('error', function forwardImgError(err) { 378 | that.emit('error', err); 379 | }); 380 | var imgFile = new Vinyl({ 381 | path: imgName, 382 | contents: result.image 383 | }); 384 | that.push(imgFile); 385 | imgStream.push(imgFile); 386 | if (retinaResult) { 387 | var retinaImgFile = new Vinyl({ 388 | path: retinaImgName, 389 | contents: retinaResult.image 390 | }); 391 | retinaResult.image.on('error', function forwardImgError(err) { 392 | that.emit('error', err); 393 | }); 394 | that.push(retinaImgFile); 395 | imgStream.push(retinaImgFile); 396 | } 397 | 398 | // Close our image stream 399 | imgStream.push(null); 400 | 401 | // Output the CSS 402 | var cssFile = new Vinyl({ 403 | path: cssName, 404 | contents: Buffer.from(cssStr) 405 | }); 406 | that.push(cssFile); 407 | cssStream.push(cssFile); 408 | cssStream.push(null); 409 | cb(); 410 | }); 411 | }; 412 | 413 | // Return output stream with two sub-streams: 414 | // - master stream includes all files 415 | // - 'css' stream for css only 416 | // - 'img' stream for images only 417 | var retStream = through2.obj(onData, onEnd); 418 | retStream.css = cssStream; 419 | retStream.img = imgStream; 420 | return retStream; 421 | } 422 | 423 | module.exports = gulpSpritesmith; 424 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gulp.spritesmith [![Build status](https://travis-ci.org/twolfson/gulp.spritesmith.svg?branch=master)](https://travis-ci.org/twolfson/gulp.spritesmith) [![Subscribe to newsletter](https://img.shields.io/badge/newsletter-subscribe-blue.svg)](http://eepurl.com/bD4qkf) 2 | 3 | Convert a set of images into a spritesheet and CSS variables via [gulp][] 4 | 5 | This is the official port of [grunt-spritesmith][], the [grunt][] equivalent of a wrapper around [spritesmith][]. 6 | 7 | [gulp]: http://gulpjs.com/ 8 | [grunt-spritesmith]: https://github.com/Ensighten/grunt-spritesmith 9 | [grunt]: http://gruntjs.com/ 10 | [spritesmith]: https://github.com/Ensighten/spritesmith 11 | 12 | ![Example input/output](docs/example.png) 13 | 14 | Alternative output formats include [SASS, Stylus, LESS, and JSON][css-formats]. 15 | 16 | [css-formats]: #spritesmithparams 17 | 18 | ### Retina support 19 | As of `gulp.spritesmith@3.5.0`, retina spritesheets/templates are supported. See the [Retina parameters section](#retina-parameters) for more information. 20 | 21 | ### Do you like `gulp.spritesmith`? 22 | [Support us via donations][support-us] or [spread word on Twitter][twitter] 23 | 24 | [support-us]: http://bit.ly/support-spritesmith-1 25 | [twitter]: https://twitter.com/intent/tweet?text=CSS%20sprites%20made%20easy%20via%20gulp.spritesmith&url=https%3A%2F%2Fgithub.com%2Ftwolfson%2Fgulp.spritesmith&via=twolfsn 26 | 27 | ## Breaking changes in 4.0.0 28 | We are normalizing sprite variables even further to convert any non-alphanumeric/non-dash/non-underscore character to a delimiter character (e.g. `-`). This allows us to support naming retina sprites with `@2x` suffixes, to prevent regressions like [grunt-spritesmith#137][]. 29 | 30 | [grunt-spritesmith#137]: https://github.com/Ensighten/grunt-spritesmith/issues/137 31 | 32 | ## Breaking changes in 5.0.0 33 | We have moved from [spritesmith-engine-spec@1.1.0][] to [spritesmith-engine-spec@2.0.0][]. This means if you use an custom engine (e.g. `gmsmith`, `canvassmith`), then you will need to upgrade it. 34 | 35 | ```bash 36 | npm install my-engine-smith@latest --save-dev 37 | ``` 38 | 39 | This is enables us to use streaming outputs from engines in a future release. 40 | 41 | Additionally, we have added support for `buffer` and `stream` content for in-memory engines (e.g. `pixelsmith`, `canvassmith`) which resolves [#53][]. 42 | 43 | [spritesmith-engine-spec@1.1.0]: https://github.com/twolfson/spritesmith-engine-spec/tree/1.1.0 44 | [spritesmith-engine-spec@2.0.0]: https://github.com/twolfson/spritesmith-engine-spec/tree/2.0.0 45 | [#53]: https://github.com/twolfson/gulp.spritesmith/issues/53 46 | 47 | ## Breaking changes in 6.0.0 48 | We have completed our integration with streaming outputs from engines. As a result, [Vinyl][] `img` files will have `stream` contents which were previously buffers. 49 | 50 | If your `img` pipeline requires `Buffer` contents, then this can be remedied via [vinyl-buffer][]: 51 | 52 | ```js 53 | // Throws error due to not supporting streams 54 | spriteData.img.pipe(imagemin()); 55 | 56 | // Back to operational 57 | var buffer = require('vinyl-buffer'); 58 | spriteData.img.pipe(buffer()).pipe(imagemin()); 59 | ``` 60 | 61 | [vinyl-buffer]: https://github.com/hughsk/vinyl-buffer 62 | 63 | ## Note for Gulp 5.0.0 upgrades 64 | Gulp 5.0.0 introduced default encodings for `src` and `dest` streams of `utf8`. 65 | 66 | Since `gulp.spritesmith` inputs and outputs images, we need to set `{encoding: false}` for the image `.src()` and `.dest()` streams. 67 | 68 | See "[Getting Started](#getting-started)" for an hands-on example. 69 | 70 | ## Getting Started 71 | Install the module with: `npm install gulp.spritesmith` 72 | 73 | ```js 74 | var gulp = require('gulp'); 75 | var spritesmith = require('gulp.spritesmith'); 76 | 77 | gulp.task('sprite', function () { 78 | var spriteData = gulp.src('images/*.png', {encoding: false}).pipe(spritesmith({ 79 | imgName: 'sprite.png', 80 | cssName: 'sprite.css' 81 | })); 82 | return spriteData.pipe(gulp.dest('path/to/output/', {encoding: false})); 83 | }); 84 | ``` 85 | 86 | ### Continuing the pipeline 87 | In addition to the `spriteData` stream, we offer individual streams for images and CSS. This allows for image optimization and CSS minification. 88 | 89 | ```js 90 | var gulp = require('gulp'); 91 | var buffer = require('vinyl-buffer'); 92 | var csso = require('gulp-csso'); 93 | var imagemin = require('gulp-imagemin'); 94 | var merge = require('merge-stream'); 95 | 96 | var spritesmith = require('gulp.spritesmith'); 97 | 98 | gulp.task('sprite', function () { 99 | // Generate our spritesheet 100 | var spriteData = gulp.src('images/*.png', {encoding: false}).pipe(spritesmith({ 101 | imgName: 'sprite.png', 102 | cssName: 'sprite.css' 103 | })); 104 | 105 | // Pipe image stream through image optimizer and onto disk 106 | var imgStream = spriteData.img 107 | // DEV: We must buffer our stream into a Buffer for `imagemin` 108 | .pipe(buffer()) 109 | .pipe(imagemin()) 110 | .pipe(gulp.dest('path/to/image/folder/', {encoding: false})); 111 | 112 | // Pipe CSS stream through CSS optimizer and onto disk 113 | var cssStream = spriteData.css 114 | .pipe(csso()) 115 | .pipe(gulp.dest('path/to/css/folder/')); 116 | 117 | // Return a merged stream to handle both `end` events 118 | return merge(imgStream, cssStream); 119 | }); 120 | ``` 121 | 122 | ## Documentation 123 | `gulp.spritesmith` presents the `spritesmith` function as its `module.exports`. 124 | 125 | ### `spritesmith(params)` 126 | [gulp][] plugin that returns a [transform stream][] with 2 [readable stream][] properties. 127 | 128 | The input/output streams interact with [Vinyl][] objects which are [gulp's][gulp] format of choice. 129 | 130 | [transform stream]: http://nodejs.org/api/stream.html#stream_class_stream_transform 131 | [readable stream]: http://nodejs.org/api/stream.html#stream_class_stream_readable 132 | [Vinyl]: https://github.com/gulpjs/vinyl 133 | 134 | - params `Object` - Container for `gulp.spritesmith` parameters 135 | - imgName `String` - Filename to save image as 136 | - Supported image extensions are `.png` and `.jpg/jpeg` (limited to specfic engines) 137 | - Image format can be overridden via `imgOpts.format` 138 | - cssName `String` - Filename to save CSS as 139 | - Supported CSS extensions are `.css` (CSS), `.sass` ([SASS][]), `.scss` ([SCSS][]), `.less` ([LESS][]), `.styl/.stylus` ([Stylus][]), and `.json` ([JSON][]) 140 | - CSS format can be overridden via `cssFormat` 141 | - imgPath `String` - Optional path to use in CSS referring to image location 142 | - padding `Number` - Optional amount of pixels to include between images 143 | - By default we use no padding between images (`0`) 144 | - An example usage can be found in the [Examples section](#padding) 145 | - algorithm `String` - Optional method for how to pack images 146 | - By default we use `binary-tree`, which packs images as efficiently as possible 147 | - An example usage can be found in the [Examples section](#algorithm) 148 | - More information can be found in the [Algorithms section](#algorithms) 149 | - algorithmOpts `Object` - Options to pass through to algorithm 150 | - For example we can skip sorting in some algorithms via `{algorithmOpts: {sort: false}}` 151 | - This is useful for sprite animations 152 | - See your algorithm's documentation for available options 153 | - https://github.com/twolfson/layout#algorithms 154 | - engine `String` - Optional image generating engine to use 155 | - By default we use `pixelsmith`, a `node` based engine that supports all common image formats 156 | - Alternative engines must be installed via `npm install` 157 | - An example usage can be found in the [Examples section](#engine) 158 | - More information can be found in the [Engines section](#engines) 159 | - engineOpts `Object` - Options to pass through to engine for settings 160 | - For example `phantomjssmith` accepts `timeout` via `{engineOpts: {timeout: 10000}}` 161 | - See your engine's documentation for available options 162 | - imgOpts `Object` - Options to pass through to engine uring export 163 | - For example `gmsmith` supports `quality` via `{imgOpts: {quality: 75}}` 164 | - See your engine's documentation for available options 165 | - cssFormat `String` - CSS format to use 166 | - By default this is the format inferred by `cssName's` extension 167 | - For example `.styl -> stylus` 168 | - For more format options, see our formatting library 169 | - https://github.com/twolfson/spritesheet-templates#templates 170 | - cssTemplate `String|Function` - CSS template to use for rendering output CSS 171 | - This overrides `cssFormat` 172 | - If a `String` is provided, it must be a path to a [handlebars][] template 173 | - An example usage can be found in the [Examples section](#handlebars-template) 174 | - If a `Function` is provided, it must have a signature of `function (data)` 175 | - An example usage can be found in the [Examples section](#template-function) 176 | - For more templating information, see the [Templating section](#templating) 177 | - cssHandlebarsHelpers `Object` - Container for helpers to register to [handlebars][] for our template 178 | - Each key-value pair is the name of a [handlebars][] helper corresponding to its function 179 | - For example, `{half: function (num) { return num/2; }` will add a [handlebars][] helper that halves numbers 180 | - cssVarMap `Function` - Mapping function for each filename to CSS variable 181 | - For more information, see [Variable mapping](#variable-mapping) 182 | - cssSpritesheetName `String` - Name to use for spritesheet related variables in preprocessor templates 183 | - cssOpts `Object` - Options to pass through to templater 184 | - For example `{cssOpts: {functions: false}}` skips output of mixins 185 | - See your template's documentation for available options 186 | - https://github.com/twolfson/spritesheet-templates#templates 187 | 188 | [SASS]: http://sass-lang.com/ 189 | [SCSS]: http://sass-lang.com/ 190 | [sass-maps]: http://sass-lang.com/documentation/file.SASS_REFERENCE.html#maps 191 | [LESS]: http://lesscss.org/ 192 | [Stylus]: http://learnboost.github.com/stylus/ 193 | [JSON]: http://json.org/ 194 | [handlebars]: http://handlebarsjs.com/ 195 | 196 | **Returns**: 197 | - spriteData [`stream.Transform`][transform stream] - Stream that outputs image and CSS as [Vinyl][] objects 198 | - spriteData.img [`stream.Readable`][readable stream] - Stream for image output as a [Vinyl][] object 199 | - `contents` will be a `Stream` 200 | - spriteData.css [`stream.Readable`][readable stream] - Stream for CSS output as a [Vinyl][] object 201 | - `contents` will be a `Buffer` 202 | 203 | ### Retina parameters 204 | `gulp.spritesmith` supports retina spritesheet generation via `retinaSrcFilter` and `retinaImgName`. If at least one of these is provided, then we will expect the other and enable retina spritesheet generation. 205 | 206 | Repeated parameters have the same properties as above but are repeated for clarity with respect to retina spritesheets. 207 | 208 | An example retina spritesheet setup can be found in the [Examples section](#retina-spritesheet). 209 | 210 | We receive both normal and retina sprites from the same `gulp.src` so please include them in your original glob. (e.g. `*.png` should include `icon-home.png` and `icon-home@2x.png`). 211 | 212 | **We strongly encourage using the `@2x` suffix for retina sprites over `-retina` or `-2x`. There are known ordering issues caused when sharing a `-` delimiter between sprite names and the retina suffix (see [grunt-spritesmith#137][]).** 213 | 214 | - params `Object` - Container for `gulp.spritesmith` parameters 215 | - retinaSrcFilter `String|String[]` - Filepaths to filter out from incoming stream for our retina spritesheet 216 | - This can be a glob as with `src` (e.g. `sprite/*@2x.png`) 217 | - The path/glob used should line up with `gulp.src` (e.g. `gulp.src('sprite/*.png', {encoding: false})`, `retinaSrcFilter: 'sprite/*@2x.png'`) 218 | - For example `sprites/*@2x.png` will filter out `sprite1@2x.png` for a separate retina spritesheet 219 | - Under the hood, we will group `sprite1.png` and `sprite1@2x.png` as a group of normal/retina sprites 220 | - retinaImgName `String` - Filename to save retina spritesheet as 221 | - retinaImgPath `String` - Optional path to use in CSS referring to image location 222 | - For example `../sprite@2x.png` will yield CSS with: 223 | - `background-image: url(../sprite@2x.png);` 224 | - padding `Number` - Padding to place to right and bottom between sprites 225 | - By default there is no padding 226 | - In retina spritesheets, this number will be doubled to maintain perspective 227 | - cssFormat - CSS format to use 228 | - By default this is the format inferred by `cssName's` extension 229 | - For example `.styl -> stylus_retina` 230 | - For more format options, see our formatting library 231 | - https://github.com/twolfson/spritesheet-templates#retina-templates 232 | - cssVarMap `Function` - Mapping function for each filename to CSS variable 233 | - This will run through normal and retina spritesheets 234 | - The name used for normal sprites dictates the group name for retina group variables (e.g. `$icon-home` will have group `$icon-home-group`) 235 | - For more information, see [Variable mapping](#variable-mapping) 236 | - cssRetinaSpritesheetName `String` - Name to use for retina spritesheet related variables in preprocessor templates 237 | - cssRetinaGroupsName `String` - Name to use for retina groups related variables in preprocessor templates 238 | 239 | **Returns**: 240 | - spriteData [`stream.Transform`][transform stream] - Stream that outputs image, retina image, and CSS as [Vinyl][] objects 241 | - spriteData.img [`stream.Readable`][readable stream] - Stream for image outputs (normal and retina) as a [Vinyl][] object 242 | - `contents` will be a `Stream` 243 | - spriteData.css [`stream.Readable`][readable stream] - Stream for retina CSS output as a [Vinyl][] object 244 | - `contents` will be a `Buffer` 245 | 246 | ### Algorithms 247 | Images can be laid out in different fashions depending on the algorithm. We use [`layout`][] to provide you as many options as possible. At the time of writing, here are your options for `algorithm`: 248 | 249 | [`layout`]: https://github.com/twolfson/layout 250 | 251 | | `top-down` | `left-right` | `diagonal` | `alt-diagonal` | `binary-tree` | 252 | |---------------------------|-------------------------------|---------------------------|-----------------------------------|---------------------------------| 253 | | ![top-down][top-down-img] | ![left-right][left-right-img] | ![diagonal][diagonal-img] | ![alt-diagonal][alt-diagonal-img] | ![binary-tree][binary-tree-img] | 254 | 255 | [top-down-img]: https://raw.githubusercontent.com/twolfson/layout/2.0.2/docs/top-down.png 256 | [left-right-img]: https://raw.githubusercontent.com/twolfson/layout/2.0.2/docs/left-right.png 257 | [diagonal-img]: https://raw.githubusercontent.com/twolfson/layout/2.0.2/docs/diagonal.png 258 | [alt-diagonal-img]: https://raw.githubusercontent.com/twolfson/layout/2.0.2/docs/alt-diagonal.png 259 | [binary-tree-img]: https://raw.githubusercontent.com/twolfson/layout/2.0.2/docs/binary-tree.png 260 | 261 | More information can be found in the [`layout`][] documentation: 262 | 263 | https://github.com/twolfson/layout 264 | 265 | ### Templating 266 | The `cssTemplate` option allows for using a custom template. An example template can be found at: 267 | 268 | https://github.com/twolfson/spritesheet-templates/blob/9.3.1/lib/templates/stylus.template.handlebars 269 | 270 | The parameters passed into your template are known as `data`. We add some normalized properties via [`spritesheet-templates`][] for your convenience. 271 | 272 | - data `Object` Container for parameters 273 | - sprites `Object[]` - Array of sprite information 274 | - name `String` - Name of the sprite file (sans extension) 275 | - x `Number` - Horizontal position of sprite's left edge in spritesheet 276 | - y `Number` - Vertical position of sprite's top edge in spritesheet 277 | - width `Number` - Width of sprite 278 | - height `Number` - Height of sprite 279 | - total_width `Number` - Width of entire spritesheet 280 | - total_height `Number` - Height of entire spritesheet 281 | - image `String` - Relative URL path from CSS to spritesheet 282 | - escaped_image `String` - URL encoded `image` 283 | - source_image `String` - Path to the original sprite file 284 | - offset_x `Number` - Negative value of `x`. Useful to `background-position` 285 | - offset_y `Number` - Negative value of `y`. Useful to `background-position` 286 | - px `Object` - Container for numeric values including `px` 287 | - x `String` - `x` suffixed with `px` 288 | - y `String` - `y` suffixed with `px` 289 | - width `String` - `width` suffixed with `px` 290 | - height `String` - `height` suffixed with `px` 291 | - total_width `String` - `total_width` suffixed with `px` 292 | - total_height `String` - `total_height` suffixed with `px` 293 | - offset_x `String` - `offset_x` suffixed with `px` 294 | - offset_y `String` - `offset_y` suffixed with `px` 295 | - spritesheet `Object` - Information about spritesheet 296 | - width `Number` - Width of entire spritesheet 297 | - total_height `Number` - Height of entire spritesheet 298 | - image `String` - Relative URL path from CSS to spritesheet 299 | - escaped_image `String` - URL encoded `image` 300 | - px `Object` - Container for numeric values including `px` 301 | - width `String` - `width` suffixed with `px` 302 | - height `String` - `height` suffixed with `px` 303 | - spritesheet_info `Object` - Container for `spritesheet` metadata and its representation 304 | - name `String` - Prefix for spritesheet variables 305 | - retina_sprites `Object[]` - Array of retina sprite information 306 | - This will only be accessible if we are generating a retina spritesheet 307 | - Properties are the same as `sprites` (e.g. `name`, `width`, `source_image`) 308 | - retina_spritesheet `Object` - Information about retina spritesheet 309 | - This will only be accessible if we are generating a retina spritesheet 310 | - Properties are the same as `spritesheet` (e.g. `width`, `px`) 311 | - retina_spritesheet_info `Object` - Container for `retina_spritesheet` metadata and its representation 312 | - This will only be accessible if we are generating a retina spritesheet 313 | - name `String` - Prefix for spritesheet variables 314 | - retina_groups `Object[]` - Array of objects that maps to normal and retina sprites 315 | - This will only be accessible if we are generating a retina spritesheet 316 | - * `Object` - Container for data about sprite mapping 317 | - name `String` - Name to refer to mapping by 318 | - index `Number` - Index of corresponding normal/retina sprites from `data.sprites`/`data.retina_sprites` 319 | - normal `Object` - Normal sprite from `data.sprites` that corresponds to our mapping 320 | - This has all the same properties as `data.sprites[*]` (e.g. `name`, `x`, `offset_y`, `px`) 321 | - retina `Object` - Retina sprite from `data.retina_sprites` that corresponds to our mapping 322 | - This has all the same properties as `data.retina_sprites[*]` (e.g. `name`, `x`, `offset_y`, `px`) 323 | - retina_groups_info `Object` - Optional container for metadata about `retina_groups` and its representation 324 | - This will only be accessible if we are generating a retina spritesheet 325 | - name `String` - Name for `retina_groups` 326 | - options `Object` - Options passed in via `cssOpts` in `gulp.spritesmith` config 327 | 328 | [`spritesheet-templates`]: https://github.com/twolfson/spritesheet-templates 329 | 330 | An example `sprite` is 331 | 332 | ```js 333 | { 334 | "name": "sprite2", 335 | "x": 10, 336 | "y": 20, 337 | "width": 20, 338 | "height": 30, 339 | "total_width": 80, 340 | "total_height": 100, 341 | "image": "nested/dir/spritesheet.png", 342 | "escaped_image": "nested/dir/spritesheet.png", 343 | "source_image": "path/to/original/sprite.png", 344 | "offset_x": -10, 345 | "offset_y": -20, 346 | "px": { 347 | "x": "10px", 348 | "y": "20px", 349 | "width": "20px", 350 | "height": "30px", 351 | "total_width": "80px", 352 | "total_height": "100px", 353 | "offset_x": "-10px", 354 | "offset_y": "-20px" 355 | } 356 | } 357 | ``` 358 | 359 | If you are defining a Handlebars template, then you can inherit from an existing template via [`handlebars-layouts`][] (e.g. `{{#extend "scss"}}`). An example usage can be found in the [Examples section](#handlebars-inheritance). 360 | 361 | [`handlebars-layouts`]: https://github.com/shannonmoeller/handlebars-layouts 362 | 363 | Example usages can be found as: 364 | 365 | - [Handlebars template](#handlebars-template) 366 | - [Handlebars inheritance](#handlebars-inheritance) 367 | - [Template function](#template-function) 368 | 369 | #### Variable mapping 370 | The `cssVarMap` option allows customization of the CSS variable names 371 | 372 | > If you would like to customize CSS selectors in the `css` template, please see https://github.com/twolfson/spritesheet-templates#css 373 | 374 | Your `cssVarMap` should be a function with the signature `function (sprite)`. It will receive the same parameters as `sprites` from [Templating](#templating) except for `escaped_image`, `offset_x`,` offset_y`, and `px`. 375 | 376 | ```js 377 | // Prefix all sprite names with `sprite-` (e.g. `home` -> `sprite-home`) 378 | cssVarMap: function (sprite) { 379 | sprite.name = 'sprite_' + sprite.name; 380 | } 381 | 382 | // Generates: 383 | // $sprite_fork_x = 0px; 384 | // $sprite_fork_y = 0px; 385 | 386 | // As oppposed to default: 387 | // $fork_x = 0px; 388 | // $fork_y = 0px; 389 | ``` 390 | 391 | ### Engines 392 | An engine can greatly improve the speed of your build (e.g. `canvassmith`) or support obscure image formats (e.g. `gmsmith`). 393 | 394 | All `spritesmith` engines adhere to a common specification: 395 | 396 | https://github.com/twolfson/spritesmith-engine-spec 397 | 398 | This repository adheres to specification version: **2.0.0** 399 | 400 | Below is a list of known engines with their tradeoffs: 401 | 402 | #### pixelsmith 403 | [`pixelsmith`][] is a `node` based engine that runs on top of [`get-pixels`][] and [`save-pixels`][]. 404 | 405 | [`pixelsmith`]: https://github.com/twolfson/pixelsmith 406 | [`get-pixels`]: https://github.com/mikolalysenko/get-pixels 407 | [`save-pixels`]: https://github.com/mikolalysenko/save-pixels 408 | 409 | **Key differences:** Doesn't support uncommon image formats (e.g. `tiff`) and not as fast as a compiled library (e.g. `canvassmith`). 410 | 411 | #### phantomjssmith 412 | [`phantomjssmith`][] is a [phantomjs][] based engine. It was originally built to provide cross-platform compatibility but has since been succeeded by [`pixelsmith`][]. 413 | 414 | **Requirements:** [phantomjs][] must be installed on your machine and on your `PATH` environment variable. Visit [the phantomjs website][phantomjs] for installation instructions. 415 | 416 | **Key differences:** `phantomjs` is cross-platform and supports all image formats. 417 | 418 | [`phantomjssmith`]: https://github.com/twolfson/phantomjssmith 419 | [phantomjs]: http://phantomjs.org/ 420 | 421 | #### canvassmith 422 | [`canvassmith`][] is a [node-canvas][] based engine that runs on top of [Cairo][]. 423 | 424 | **Requirements:** [Cairo][] and [node-gyp][] must be installed on your machine. 425 | 426 | Instructions on how to install [Cairo][] are provided in the [node-canvas wiki][]. 427 | 428 | [node-gyp][] should be installed via `npm`: 429 | 430 | ```bash 431 | npm install -g node-gyp 432 | ``` 433 | 434 | **Key differences:** `canvas` has the best performance (useful for over 100 sprites). However, it is `UNIX` only. 435 | 436 | [`canvassmith`]: https://github.com/twolfson/canvassmith 437 | [node-canvas]: https://github.com/learnboost/node-canvas 438 | [Cairo]: http://cairographics.org/ 439 | [node-canvas wiki]: (https://github.com/LearnBoost/node-canvas/wiki/_pages 440 | [node-gyp]: https://github.com/TooTallNate/node-gyp/ 441 | 442 | #### gmsmith 443 | [`gmsmith`][] is a [`gm`][] based engine that runs on top of either [Graphics Magick][] or [Image Magick][]. 444 | 445 | **Requirements:** Either [Graphics Magick][] or [Image Magick][] must be installed on your machine. 446 | 447 | For the best results, install from the site rather than through a package manager (e.g. `apt-get`). This avoids potential transparency issues which have been reported. 448 | 449 | [Image Magick][] is implicitly discovered. However, you can explicitly use it via `engineOpts` 450 | 451 | ```js 452 | { 453 | engineOpts: { 454 | imagemagick: true 455 | } 456 | } 457 | ``` 458 | 459 | **Key differences:** `gmsmith` allows for configuring image quality whereas others do not. 460 | 461 | [`gmsmith`]: https://github.com/twolfson/gmsmith 462 | [`gm`]: https://github.com/aheckmann/gm 463 | [Graphics Magick]: http://www.graphicsmagick.org/ 464 | [Image Magick]: http://imagemagick.org/ 465 | 466 | ## Examples 467 | ### Algorithm 468 | In this example, we are using the `alt-diagonal` algorithm to guarantee no overlap if images overflow. 469 | 470 | **Configuration:** 471 | 472 | ```js 473 | { 474 | imgName: 'sprite.png', 475 | cssName: 'sprite.styl', 476 | algorithm: 'alt-diagonal' 477 | } 478 | ``` 479 | 480 | **Output:** 481 | 482 | ![algorithm spritesheet](docs/examples/algorithm/sprite.png) 483 | 484 | ### Engine 485 | In this example, we are using the `phantomjssmith` engine as an alternative to the `pixelsmith` default. 486 | 487 | **Requirements:** 488 | 489 | Install `phantomjssmith` to our `node_modules` via `npm install`. 490 | 491 | ```bash 492 | npm install phantomjssmith 493 | ``` 494 | 495 | Alternatively, we can use `--save` or `--save-dev` to save to our `package.json's dependencies` or `devDependenices`. 496 | 497 | ```bash 498 | npm install phantomjssmith --save # Updates {"dependencies": {"phantomjssmith": "1.2.3"}} 499 | npm install phantomjssmith --save-dev # Updates {"devDependencies": {"phantomjssmith": "1.2.3"}} 500 | ``` 501 | 502 | **Configuration:** 503 | 504 | ```js 505 | // var phantomjssmith = require('phantomjssmith'); 506 | { 507 | imgName: 'sprite.png', 508 | cssName: 'sprite.styl', 509 | engine: phantomjssmith 510 | } 511 | ``` 512 | 513 | **Output:** 514 | 515 | ![engine spritesheet](docs/examples/engine/sprite.png) 516 | 517 | ### Padding 518 | The `padding` option allows for inserting spacing between images. 519 | 520 | **Configuration:** 521 | 522 | ```js 523 | { 524 | imgName: 'sprite.png', 525 | cssName: 'sprite.styl', 526 | padding: 20 // Exaggerated for visibility, normal usage is 1 or 2 527 | } 528 | ``` 529 | 530 | **Output:** 531 | 532 | ![padding spritesheet](docs/examples/padding/sprite.png) 533 | 534 | ### Retina spritesheet 535 | In this example, we will use generate a normal and retina spritesheet via the `retinaSrcFilter` and `retinaImgName` parameters. 536 | 537 | **Configuration:** 538 | 539 | ```js 540 | { 541 | // This will filter out `fork@2x.png`, `github@2x.png`, ... for our retina spritesheet 542 | // The normal spritesheet will now receive `fork.png`, `github.png`, ... 543 | retinaSrcFilter: ['images/*@2x.png'], 544 | imgName: 'sprite.png', 545 | retinaImgName: 'sprite@2x.png', 546 | cssName: 'sprite.styl' 547 | } 548 | ``` 549 | 550 | **Normal spritesheet:** 551 | 552 | ![Normal spritesheet](docs/examples/retina/sprite.png) 553 | 554 | **Retina spritesheet:** 555 | 556 | ![Retina spritesheet](docs/examples/retina/sprite@2x.png) 557 | 558 | ### Handlebars template 559 | In this example, we will use `cssTemplate` with a `handlebars` template to generate CSS that uses `:before` selectors. 560 | 561 | **Template:** 562 | 563 | ```handlebars 564 | {{#sprites}} 565 | .icon-{{name}}:before { 566 | display: block; 567 | background-image: url({{{escaped_image}}}); 568 | background-position: {{px.offset_x}} {{px.offset_y}}; 569 | width: {{px.width}}; 570 | height: {{px.height}}; 571 | } 572 | {{/sprites}} 573 | ``` 574 | 575 | **Configuration:** 576 | 577 | ```js 578 | { 579 | imgName: 'sprite.png', 580 | cssName: 'sprite.css', 581 | cssTemplate: 'handlebarsStr.css.handlebars' 582 | } 583 | ``` 584 | 585 | **Output:** 586 | 587 | ```css 588 | .icon-fork:before { 589 | display: block; 590 | background-image: url(sprite.png); 591 | background-position: 0px 0px; 592 | width: 32px; 593 | height: 32px; 594 | } 595 | .icon-github:before { 596 | /* ... */ 597 | ``` 598 | 599 | ### Handlebars inheritance 600 | In this example, we will extend the SCSS template to provide minimal variables. The JSON at the front comes from the original template and is required to provide consistent casing and default options. 601 | 602 | Different block sections for each template are documented in: 603 | 604 | https://github.com/twolfson/spritesheet-templates 605 | 606 | **Template:** 607 | 608 | ```handlebars 609 | { 610 | // Default options 611 | 'functions': true, 612 | 'variableNameTransforms': ['dasherize'] 613 | } 614 | 615 | {{#extend "scss"}} 616 | {{#content "sprites"}} 617 | {{#each sprites}} 618 | ${{strings.name}}: ({{px.x}}, {{px.y}}, {{px.offset_x}}, {{px.offset_y}}, {{px.width}}, {{px.height}}, {{px.total_width}}, {{px.total_height}}, '{{{escaped_image}}}', '{{name}}', ); 619 | {{/each}} 620 | {{/content}} 621 | {{#content "spritesheet"}} 622 | ${{spritesheet_info.strings.name_sprites}}: ({{#each sprites}}${{strings.name}}, {{/each}}); 623 | ${{spritesheet_info.strings.name}}: ({{spritesheet.px.width}}, {{spritesheet.px.height}}, '{{{spritesheet.escaped_image}}}', ${{spritesheet_info.strings.name_sprites}}, ); 624 | {{/content}} 625 | {{/extend}} 626 | ``` 627 | 628 | **Configuration:** 629 | 630 | ```js 631 | { 632 | imgName: 'sprite.png', 633 | cssName: 'sprite.scss', 634 | cssTemplate: 'handlebarsInheritance.scss.handlebars' 635 | } 636 | ``` 637 | 638 | **Output:** 639 | 640 | ```scss 641 | $fork: (0px, 0px, 0px, 0px, 32px, 32px, 64px, 64px, 'sprite.png', 'fork', ); 642 | $github: (32px, 0px, -32px, 0px, 32px, 32px, 64px, 64px, 'sprite.png', 'github', ); 643 | $twitter: (0px, 32px, 0px, -32px, 32px, 32px, 64px, 64px, 'sprite.png', 'twitter', ); 644 | $spritesheet-sprites: ($fork, $github, $twitter, ); 645 | $spritesheet: (64px, 64px, 'sprite.png', $spritesheet-sprites, ); 646 | /* ... */ 647 | ``` 648 | 649 | ### Template function 650 | In this example, we will use `cssTemplate` with a custom function that generates YAML. 651 | 652 | **Configuration:** 653 | 654 | ```js 655 | // var yaml = require('js-yaml'); 656 | { 657 | imgName: 'sprite.png', 658 | cssName: 'sprite.yml', 659 | cssTemplate: function (data) { 660 | // Convert sprites from an array into an object 661 | var spriteObj = {}; 662 | data.sprites.forEach(function (sprite) { 663 | // Grab the name and store the sprite under it 664 | var name = sprite.name; 665 | spriteObj[name] = sprite; 666 | 667 | // Delete the name from the sprite 668 | delete sprite.name; 669 | }); 670 | 671 | // Return stringified spriteObj 672 | return yaml.safeDump(spriteObj); 673 | } 674 | } 675 | ``` 676 | 677 | **Output:** 678 | 679 | ```yaml 680 | fork: 681 | x: 0 682 | 'y': 0 683 | width: 32 684 | height: 32 685 | source_image: /home/todd/github/gulp.spritesmith/docs/images/fork.png 686 | image: sprite.png 687 | total_width: 64 688 | total_height: 64 689 | escaped_image: sprite.png 690 | offset_x: -0.0 691 | offset_y: -0.0 692 | px: 693 | x: 0px 694 | 'y': 0px 695 | offset_x: 0px 696 | offset_y: 0px 697 | height: 32px 698 | width: 32px 699 | total_height: 64px 700 | total_width: 64px 701 | github: 702 | # ... 703 | ``` 704 | 705 | ### Cache busting 706 | `gulp.spritesmith` doesn't directly support cache busting but [`gulp-spritesmash`][] is a plugin that takes `gulp.spritesmith's` output and generates cache busted filenames and CSS URLs. Here's an example usage: 707 | 708 | 709 | 710 | ```js 711 | var gulp = require('gulp'); 712 | var buffer = require('vinyl-buffer'); 713 | var spritesmash = require('gulp-spritesmash'); 714 | var spritesmith = require('gulp.spritesmith'); 715 | 716 | gulp.task('sprite', function () { 717 | return gulp.src('images/*.png', {encoding: false}) 718 | .pipe(spritesmith({ 719 | imgName: 'sprite.png', 720 | cssName: 'sprite.css' 721 | })) 722 | .pipe(buffer()) 723 | .pipe(spritesmash()); 724 | .pipe(gulp.dest('path/to/output/', {encoding: false})); 725 | }); 726 | ``` 727 | 728 | [`gulp-spritesmash`]: https://github.com/MasterOfMalt/gulp-spritesmash 729 | 730 | ## Contributing 731 | In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint via `npm run lint` and test via `npm test`. 732 | 733 | ## Attribution 734 | GitHub and Twitter icons were taken from [Alex Peattie's JustVector Social Icons][justvector]. 735 | 736 | Fork designed by [P.J. Onori][onori] from The Noun Project. 737 | 738 | [justvector]: http://alexpeattie.com/projects/justvector_icons/ 739 | [noun-fork-icon]: http://thenounproject.com/noun/fork/#icon-No2813 740 | [onori]: http://thenounproject.com/somerandomdude 741 | 742 | ## Unlicense 743 | As of Feb 09 2014, Todd Wolfson has released this repository and its contents to the public domain. 744 | 745 | It has been released under the [UNLICENSE][]. 746 | 747 | [UNLICENSE]: UNLICENSE 748 | --------------------------------------------------------------------------------