gulp.spritesmithでRetina対応をする

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
    • data
      • sprite
    • sass
      • _sprite.scss
      • style.scss
  • 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;
  }
}

CSSスプライトのRetina対応のデモページ
 

【参考サイト】

 

このエントリーをはてなブックマークに追加

関連記事

コメントを残す

メールアドレスが公開されることはありません。
* が付いている欄は必須項目です

CAPTCHA


コメントが承認されるまで時間がかかります。

2024年3月
 12
3456789
10111213141516
17181920212223
24252627282930
31