CSSスプライトを生成するgulp.spritesmithでRetina対応をする方法をメモ。
サンプルコード
gulp.spritesmithの基本的な使い方については以前に記事を投稿していますので、以下をご確認ください。
GulpでCSSスプライトを作成する
今回はgulp.spritesmithとgulp-sassを使用します。
インストール後、gulpfile.jsを以下のように設定します。
gulpfile.js
var gulp = require('gulp'), sass = require('gulp-sass'), spritesmith = require('gulp.spritesmith'); /* ================================================== Sass ================================================== */ gulp.task('sass', function () { gulp.src(['./dev/sass/**/*.scss', '!./dev/sass/**/_*.scss']) .pipe(sass({ outputStyle : 'expanded' })) .pipe(gulp.dest('./dev/assets/css/')); }); /* ================================================== Sprite ================================================== */ gulp.task('sprite', function () { var spriteData = gulp.src('./dev/data/sprite/*.png').pipe(spritesmith({ imgName: 'sprite.png', cssName: '_sprite.scss', imgPath: '../images/common/sprite.png', // cssFormat: 'scss', cssVarMap: function (sprite) { sprite.name = 'sprite-' + sprite.name; }, retinaSrcFilter: './dev/data/sprite/*@2x.png', retinaImgName: 'sprite@2x.png', retinaImgPath: '../images/common/sprite@2x.png' })); spriteData.img.pipe(gulp.dest('./dev/assets/images/common/')); spriteData.css.pipe(gulp.dest('./dev/sass/')); });
注意点として、cssFormatを設定しているとRetina分の変数などが生成されていませんでした。
ディレクトリ構成は以下のようになっています。
- dev
- assets
- css
- style.css
- images
- common
- sprite.png
- sprite@2x.png
- common
- css
- data
- sprite
- sass
- _sprite.scss
- style.scss
- assets
- gulpfile.js
sprite.pngとsprite@2x.png、_sprite.scssはGulpで生成するので、最初の段階ではありません。
dev/data/sprite/のディレクトリにスプライト画像で使用する画像を格納します。
通常用とRetina用の2種類を用意しますが、Retina用の画像は通常用のファイル名の末尾に「@2x」を追加してください。
例えば通常用の画像がstar.pngの場合、Retina用はstar@2x.pngになります。
dev/data/sprite/にスプライト用の画像を追加して、コマンドプロンプトで以下を実行します。
gulp sprite
特にエラーが出なければ成功です。
今回試してみて、「Error: Normal sprite has inconsistent size with retina sprite. “xxxxx\heart.png” is 20×17 while “xxxxx\heart@2x.png” is 40×36.」のようなエラーが出ました。
通常用の画像とRetina用の画像サイズが正確に合っていないと駄目なようで、heart.pngのサイズを20×18修正して再度試すとエラーが出ませんでした。
今回以下のようなスプライト画像とSassファイルが生成できました。
sprite.png
sprite@2x.png
_sprite.scss
// SCSS variables are information about icon's compiled state, stored under its original file name // // .icon-home { // width: $icon-home-width; // } // // The large array-like variables contain all information about a single icon // $icon-home: x y offset_x offset_y width height total_width total_height image_path; // // At the bottom of this section, we provide information about the spritesheet itself // $spritesheet: width height image $spritesheet-sprites; $sprite-check-name: 'sprite-check'; $sprite-check-x: 0px; $sprite-check-y: 0px; $sprite-check-offset-x: 0px; $sprite-check-offset-y: 0px; $sprite-check-width: 20px; $sprite-check-height: 20px; $sprite-check-total-width: 40px; $sprite-check-total-height: 38px; $sprite-check-image: '../images/common/sprite.png'; $sprite-check: (0px, 0px, 0px, 0px, 20px, 20px, 40px, 38px, '../images/common/sprite.png', 'sprite-check', ); $sprite-heart-name: 'sprite-heart'; $sprite-heart-x: 0px; $sprite-heart-y: 20px; $sprite-heart-offset-x: 0px; $sprite-heart-offset-y: -20px; $sprite-heart-width: 20px; $sprite-heart-height: 18px; $sprite-heart-total-width: 40px; $sprite-heart-total-height: 38px; $sprite-heart-image: '../images/common/sprite.png'; $sprite-heart: (0px, 20px, 0px, -20px, 20px, 18px, 40px, 38px, '../images/common/sprite.png', 'sprite-heart', ); $sprite-search-name: 'sprite-search'; $sprite-search-x: 20px; $sprite-search-y: 20px; $sprite-search-offset-x: -20px; $sprite-search-offset-y: -20px; $sprite-search-width: 20px; $sprite-search-height: 16px; $sprite-search-total-width: 40px; $sprite-search-total-height: 38px; $sprite-search-image: '../images/common/sprite.png'; $sprite-search: (20px, 20px, -20px, -20px, 20px, 16px, 40px, 38px, '../images/common/sprite.png', 'sprite-search', ); $sprite-star-name: 'sprite-star'; $sprite-star-x: 20px; $sprite-star-y: 0px; $sprite-star-offset-x: -20px; $sprite-star-offset-y: 0px; $sprite-star-width: 20px; $sprite-star-height: 19px; $sprite-star-total-width: 40px; $sprite-star-total-height: 38px; $sprite-star-image: '../images/common/sprite.png'; $sprite-star: (20px, 0px, -20px, 0px, 20px, 19px, 40px, 38px, '../images/common/sprite.png', 'sprite-star', ); $sprite-check-2x-name: 'sprite-check@2x'; $sprite-check-2x-x: 0px; $sprite-check-2x-y: 0px; $sprite-check-2x-offset-x: 0px; $sprite-check-2x-offset-y: 0px; $sprite-check-2x-width: 40px; $sprite-check-2x-height: 40px; $sprite-check-2x-total-width: 80px; $sprite-check-2x-total-height: 76px; $sprite-check-2x-image: '../images/common/sprite@2x.png'; $sprite-check-2x: (0px, 0px, 0px, 0px, 40px, 40px, 80px, 76px, '../images/common/sprite@2x.png', 'sprite-check@2x', ); $sprite-heart-2x-name: 'sprite-heart@2x'; $sprite-heart-2x-x: 0px; $sprite-heart-2x-y: 40px; $sprite-heart-2x-offset-x: 0px; $sprite-heart-2x-offset-y: -40px; $sprite-heart-2x-width: 40px; $sprite-heart-2x-height: 36px; $sprite-heart-2x-total-width: 80px; $sprite-heart-2x-total-height: 76px; $sprite-heart-2x-image: '../images/common/sprite@2x.png'; $sprite-heart-2x: (0px, 40px, 0px, -40px, 40px, 36px, 80px, 76px, '../images/common/sprite@2x.png', 'sprite-heart@2x', ); $sprite-search-2x-name: 'sprite-search@2x'; $sprite-search-2x-x: 40px; $sprite-search-2x-y: 40px; $sprite-search-2x-offset-x: -40px; $sprite-search-2x-offset-y: -40px; $sprite-search-2x-width: 40px; $sprite-search-2x-height: 32px; $sprite-search-2x-total-width: 80px; $sprite-search-2x-total-height: 76px; $sprite-search-2x-image: '../images/common/sprite@2x.png'; $sprite-search-2x: (40px, 40px, -40px, -40px, 40px, 32px, 80px, 76px, '../images/common/sprite@2x.png', 'sprite-search@2x', ); $sprite-star-2x-name: 'sprite-star@2x'; $sprite-star-2x-x: 40px; $sprite-star-2x-y: 0px; $sprite-star-2x-offset-x: -40px; $sprite-star-2x-offset-y: 0px; $sprite-star-2x-width: 40px; $sprite-star-2x-height: 38px; $sprite-star-2x-total-width: 80px; $sprite-star-2x-total-height: 76px; $sprite-star-2x-image: '../images/common/sprite@2x.png'; $sprite-star-2x: (40px, 0px, -40px, 0px, 40px, 38px, 80px, 76px, '../images/common/sprite@2x.png', 'sprite-star@2x', ); $spritesheet-width: 40px; $spritesheet-height: 38px; $spritesheet-image: '../images/common/sprite.png'; $spritesheet-sprites: ($sprite-check, $sprite-heart, $sprite-search, $sprite-star, ); $spritesheet: (40px, 38px, '../images/common/sprite.png', $spritesheet-sprites, ); $retina-spritesheet-width: 80px; $retina-spritesheet-height: 76px; $retina-spritesheet-image: '../images/common/sprite@2x.png'; $retina-spritesheet-sprites: ($sprite-check-2x, $sprite-heart-2x, $sprite-search-2x, $sprite-star-2x, ); $retina-spritesheet: (80px, 76px, '../images/common/sprite@2x.png', $retina-spritesheet-sprites, ); // These "retina group" variables are mappings for the naming and pairing of normal and retina sprites. // // The list formatted variables are intended for mixins like `retina-sprite` and `retina-sprites`. $sprite-check-group-name: 'sprite-check'; $sprite-check-group: ('sprite-check', $sprite-check, $sprite-check-2x, ); $sprite-heart-group-name: 'sprite-heart'; $sprite-heart-group: ('sprite-heart', $sprite-heart, $sprite-heart-2x, ); $sprite-search-group-name: 'sprite-search'; $sprite-search-group: ('sprite-search', $sprite-search, $sprite-search-2x, ); $sprite-star-group-name: 'sprite-star'; $sprite-star-group: ('sprite-star', $sprite-star, $sprite-star-2x, ); $retina-groups: ($sprite-check-group, $sprite-heart-group, $sprite-search-group, $sprite-star-group, ); // The provided mixins are intended to be used with the array-like variables // // .icon-home { // @include sprite-width($icon-home); // } // // .icon-email { // @include sprite($icon-email); // } // // Example usage in HTML: // // `display: block` sprite: // <div class="icon-home"></div> // // To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class: // // // CSS // .icon { // display: inline-block; // } // // // HTML // <i class="icon icon-home"></i> @mixin sprite-width($sprite) { width: nth($sprite, 5); } @mixin sprite-height($sprite) { height: nth($sprite, 6); } @mixin sprite-position($sprite) { $sprite-offset-x: nth($sprite, 3); $sprite-offset-y: nth($sprite, 4); background-position: $sprite-offset-x $sprite-offset-y; } @mixin sprite-image($sprite) { $sprite-image: nth($sprite, 9); background-image: url(#{$sprite-image}); } @mixin sprite($sprite) { @include sprite-image($sprite); @include sprite-position($sprite); @include sprite-width($sprite); @include sprite-height($sprite); } // The `retina-sprite` mixin sets up rules and a media query for a sprite/retina sprite. // It should be used with a "retina group" variable. // // The media query is from CSS Tricks: https://css-tricks.com/snippets/css/retina-display-media-query/ // // $icon-home-group: ('icon-home', $icon-home, $icon-home-2x, ); // // .icon-home { // @include retina-sprite($icon-home-group); // } @mixin sprite-background-size($sprite) { $sprite-total-width: nth($sprite, 7); $sprite-total-height: nth($sprite, 8); background-size: $sprite-total-width $sprite-total-height; } @mixin retina-sprite($retina-group) { $normal-sprite: nth($retina-group, 2); $retina-sprite: nth($retina-group, 3); @include sprite($normal-sprite); @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { @include sprite-image($retina-sprite); @include sprite-background-size($normal-sprite); } } // The `sprites` mixin generates identical output to the CSS template // but can be overridden inside of SCSS // // @include sprites($spritesheet-sprites); @mixin sprites($sprites) { @each $sprite in $sprites { $sprite-name: nth($sprite, 10); .#{$sprite-name} { @include sprite($sprite); } } } // The `retina-sprites` mixin generates a CSS rule and media query for retina groups // This yields the same output as CSS retina template but can be overridden in SCSS // // @include retina-sprites($retina-groups); @mixin retina-sprites($retina-groups) { @each $retina-group in $retina-groups { $sprite-name: nth($retina-group, 1); .#{$sprite-name} { @include retina-sprite($retina-group); } } }
実際にRetina対応させたCSSスプライトを使ってみます。
HTML
<i class="icon icon_star"></i> <i class="icon icon_heart"></i> <i class="icon icon_check"></i> <i class="icon icon_search"></i>
Sassでの設定方法は_sprite.scss内にコメントでも記載がありますが、mixinの「retina-sprite」と変数の「$sprite-XXX-group」を使用します。
style.scss
@import "sprite"; .icon { display: inline-block; } .icon_star { @include retina-sprite($sprite-star-group); } .icon_heart { @include retina-sprite($sprite-heart-group); } .icon_check { @include retina-sprite($sprite-check-group); } .icon_search { @include retina-sprite($sprite-search-group); }
これでSassをコンパイルすると、以下のようにCSSが生成されました。
style.css
.icon { display: inline-block; } .icon_star { background-image: url(../images/common/sprite.png); background-position: -20px 0px; width: 20px; height: 19px; } @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { .icon_star { background-image: url(../images/common/sprite@2x.png); background-size: 40px 38px; } } .icon_heart { background-image: url(../images/common/sprite.png); background-position: 0px -20px; width: 20px; height: 18px; } @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { .icon_heart { background-image: url(../images/common/sprite@2x.png); background-size: 40px 38px; } } .icon_check { background-image: url(../images/common/sprite.png); background-position: 0px 0px; width: 20px; height: 20px; } @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { .icon_check { background-image: url(../images/common/sprite@2x.png); background-size: 40px 38px; } } .icon_search { background-image: url(../images/common/sprite.png); background-position: -20px -20px; width: 20px; height: 16px; } @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { .icon_search { background-image: url(../images/common/sprite@2x.png); background-size: 40px 38px; } }
【参考サイト】
コメントが承認されるまで時間がかかります。