2013/07/30更新

[CSS] CompassでCSSスプライト対応、Retina対応を行う。Web上にあるMixinは自分の要件に合わずだったので、作りました。

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

こんにちは、@yoheiMuneです。
今日はCompassを使った簡単CSSスプライト画像+CSSクラスの生成と、Retina対応する方法をブログに書きたいと思います。

画像



Compassは、自動でCSSスプライトを作成できて便利

最近のお仕事で、フロントエンドのパフォーマンス改善を行うことがありますが、その改善点の中で画像をスプライト化して、リクエスト数を減らすという対策を行います。

スプライト画像そのものを作るのも面倒だし、そのスプライト画像を使ったCSSを書くのもかなり面倒。 その面倒な作業を全て自動かしてくれる機能がCompassにあります。その機能を紹介したい&カスタマイズしてより使いやすくしたいというのが今回のブログの意図です。

Web上で「CompassでSpriteを行う、Retina対応を行う」という情報を色々と読んだのですが、以下の点がもったいないなぁと思ったので、今回自分で作った次第です。
  • Sprite画像1枚にしか対応していないMixinがちらほら。
  • Retina対応のCSSを出力するMixinはあるが、Sprite内の画像全部のCSSを自動で出してくれるMixinが見当たらない。
  • Retina対応ということで、「2で割る」ことが固定されている。後続で紹介しますが「1.5」で割りたいという思いがありました。
それでは、本題です!!



今回の環境や構成

今回のCompassでスプライトを行う記載は、以下のディレクトリ構成をしているとします。

  • ProjectRoot
    • Gruntfile.js
    • images
      • icons
        • facebook.png
        • twitter.png
        • mixi.png
      • icons_retina
        • facebook.png
        • twitter.png
        • mixi.png
    • scss
      • main.scss
    • css
      • main.css


またCompassのコンパイルを今回は、Gruntfile.jsで行いました。以下がGruntfile.jsの内容となります。
/* Gruntfile.js */
module.exports = function(grunt) {

  // load Npm Tasks.
  grunt.loadNpmTasks('grunt-contrib-compass');

  grunt.initConfig({
    // compass
    compass: {
      sprite01: {
        options: {
          sassDir: 'scss',
          cssDir: 'css',
          imageDir: 'images/',
        }
      }
    },
  });

  // Default task.
  grunt.registerTask('default', ['compass']);
};
#以下のgruntコマンドでコンパイルします
$ grunt



CompassでSprite画像生成とSprite画像を使ったCSSの生成を自動で行う

まずはRetina対応しない普通の画像です。画像を等倍表示するSprite化のやり方は色々とありますが、以下のようにやるのが一番簡単かなぁと思います。
$experimental-support-for-opera : false !default;         // -o-は出力しない
$experimental-support-for-microsoft : false !default;     // -ms-は出力しない
@import "compass";

// スプライト画像生成と、スプライト画像を使ったCSSの出力
// たった2行!!
@import "icons/*.png";
@include all-icons-sprites(d);
たったこれだけで、「images/icons/」のディレクトリに配置された3つのアイコンのSprite画像が生成され、それに対応したCSSが出力されます。

(生成されたSprite画像例)
  • ProjectRoot
    • Gruntfile.js
    • images
      • icons
        • facebook.png
        • twitter.png
        • mixi.png
    • icons-se594799de0.png

(生成されたスタイル内容)
.icons-sprite, .icons-facebook, .icons-mixi, .icons-twitter {
  background-image: url('/images/icons-se594799de0.png');
  background-repeat: no-repeat;
}

.icons-facebook {
  background-position: 0 0;
  height: 88px;
  width: 88px;
}

.icons-mixi {
  background-position: 0 -88px;
  height: 88px;
  width: 88px;
}

.icons-twitter {
  background-position: 0 -176px;
  height: 88px;
  width: 88px;
}
とこんな感じで、scssの2行の記述で、Sprite画像とそれに伴うCSSが生成されてしまいました。 恐ろしく便利な記述です。またsprite画像の中身が変わると、生成されるSprite画像のファイル名も変わるので、キャッシュバスター機能も付いてます。便利便利☆

なお、CompassでSprite画像を生成する方法で上記の2行のみを書くのは、Compassの公式チュートリアルには無いので亜種なやり方かも。CompassでSprite作成のより詳しい情報は、以下のリンクからご確認ください。

http://compass-style.org/help/tutorials/spriting/



CompassでSprite作成する際にRetina対応を行う

さてここまでがCompassの便利な機能だったのですが、Retina対応したSprite画像とそのCSSを生成するには、mixinを作ってちょっと頑張る必要があります。 以下のようなMixinを作成して、sprite-mapからRetina対応用のCSSクラスを出力するようにしました。
//=======================
//  SpriteMapと画像Nameを受け取り、CSSを出力するMixin
// 
// @param map(require)             :sprite-map変数
// @param name(require)            :spriteを構成する画像名
// @param pixelRaito(optional)     :Retina対応する場合には、2を指定する
// 
// ※注意点
// pixelRaitoに1以外を指定する場合には、Spriteを生成する為の画像の高さと横幅が
// picelRaitoで割り切れるようにする必要があります。
// 例えば、Retina対応用の画像の場合は、Spriteを生成する為の画像の高さとよ横幅は
// 偶数にする必要があります。
// 
//=======================
@mixin sprite-background($map, $name, $pixelRaito:1) {

    // 画像の大きさ(必要があれば)
    // width: round(image-width(sprite-file($map, $name)) / $pixelRaito);
    // height: round(image-height(sprite-file($map, $name)) / $pixelRaito);

    // 背景パス、サイズ
    background-image: $map;
    background-repeat: none;
    $spritePath: sprite-path($map);
    $spriteWidth: round(image-width($spritePath) / $pixelRaito);
    $spriteHeight: round(image-height($spritePath) / $pixelRaito);
    @include background-size($spriteWidth, $spriteHeight);

    // 背景ポジション
    $pos: sprite-position($map, $name);
    background-position: (nth($pos, 1) / $pixelRaito) (nth($pos, 2) / $pixelRaito);
}


//=======================
// Retina用画像を用いたスプライトとCSSクラスの生成するMixin。
// このMixinを使うことで、指定したmapに含まれる画像に対するCSSを全て出力することが出来ます。
// 
// @param map(require)           : sprite-map
// @param pixelRaito(optional)   : ピクセルRaito(Retina用の場合は、2を指定する)
// @param placeHolder(optional)  : CSSクラスではなく、%セレクタのプレースホルダーを出力する場合はtrue
// 
//=======================
@mixin output-css-from-sprite($map, $pixelRaito:1, $placeHolder:false) {

    // ディレクトリ名をCSSクラスのプレフィックスに付けます
    $dirName: sprite-map-name($map);

    // Spriteの中身を1件ずつCSS出力を行います
    @each $name in sprite-names($map) {

        // %xxとプレースホルダーを出力するか、CSSクラスを出力するかにより
        // 処理を分けます。
        @if $placeHolder {
            %#{$dirName}_#{$name} {
                @include sprite-background($map, $name, $pixelRaito);
            }            
        } @else {
            .#{$dirName}_#{$name} {
                @include sprite-background($map, $name, $pixelRaito);
            }            
        }
    }
}
そして上記Mixinを使う側は、こんな感じに記載します。
// Retina対応したSprite画像を生成します。
$sprites_icons_retina_map: sprite-map("icons_retina/*.png");
$sprites_icons_retina_map_img: sprite-url($sprites_icons_retina_map);

// Spriteのmap情報を元に、Retina対応したCSSクラスを出力します。
@include output-css-from-sprite($sprites_icons_retina_map, 2);
3行で通常より1行増えてしまいましたが、こんな感じで使えます。
出力結果は以下のようになります。
.icons_retina_facebook {
  background-image: url('/images/icons_retina-s62de215f75.png');
  background-repeat: none;
  -webkit-background-size: 44px, 132px;
  -moz-background-size: 44px, 132px;
  background-size: 44px, 132px;
  background-position: 0 -44px;
}
.icons_retina_mixi {
  background-image: url('/images/icons_retina-s62de215f75.png');
  background-repeat: none;
  -webkit-background-size: 44px, 132px;
  -moz-background-size: 44px, 132px;
  background-size: 44px, 132px;
  background-position: 0 0;
}
.icons_retina_twitter {
  background-image: url('/images/icons_retina-s62de215f75.png');
  background-repeat: none;
  -webkit-background-size: 44px, 132px;
  -moz-background-size: 44px, 132px;
  background-size: 44px, 132px;
  background-position: 0 -88px;
}

Mixinは独立性高く作っているので、プロジェクト関係なく使えるのではないかと思います。なのでMixinさせプロジェクトの最初に定義してしまえば、あとはSpriteしたい部分で上記の3行を記載すれば出来る(はず)です。



余談ですが、Retina対応で1.5倍の画像を使う?

今回のブログ記事と直接関係ないのですが、Retina対応する場合に画像を縦横2倍サイズを用意することが多いと思いますが、 最近の仕事で縦横1.5倍のサイズを使う機会がありました。
2倍に比べると確かに少し汚くなるのですが、2倍の場合よりサイズが9/16となるので、パフォーマンスを考えて1.5倍となりました。

「Retina対応 = 画像の縦横2倍」と思っていた自分からすると、目から鱗な感じでした。
そして1.5倍画像をSpriteする場合にも、上記のMixinが使えます。ちょっと便利☆



最後に

なんとか使いやすいと感じるMixinが作れてよかったという思いです。 CompassでSprite生成は便利なのですが、プロジェクト固有の要件を加えようとするとMixinを作る必要があったりと、急に複雑になります。
今回の記事が、Web制作する誰かの役に立てばいいなぁと思う今日この頃です。

最後までご覧頂きましてありがとうございました。





こんな記事もいかがですか?

RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。