Compassを使ったSpriting最適化 [Retina+SVG編]


[The end of sprites; the rise of SVG | Optical Cortex](http://opticalcortex.com/the-end-of-sprites-the-rise-of-svg/) 
というポストを見て、Compassにインラインデータを埋め込む機能があることを知りました。

- [Compass Inline Data Helpers | Compass Documentation](http://compass-style.org/reference/compass/helpers/inline-data/)


これを使えば、これまで温めてきたCompassのSpritingに、SVGのサポートも加えることが出来そうです。


## ディレクトリ構成

Compassのconfigで指定した画像フォルダ (ここでは `sprites` ) に、

```
sprites/icons_1x
├ icon-facebook.png
├ icon-twitter.png
└ icon-github.png

sprites/icons_2x
├ icon-facebook.png
├ icon-twitter.png
└ icon-github.png
```

`sprites` の隣に `svg` を作って、

```
svg/icons_svg
├ icon-facebook.svg
├ icon-twitter.svg
└ icon-github.svg
```

というように配置します。


## mixin

前回、[Compassを使ったSpriting最適化 [Retina編]](http://qiita.com/soheikitada/items/f7a6e71daefe1b199517) で書いたmixinをベースに、SVGのサポートを加えます。

```scss
@import "compass/utilities/sprites";

// Mixin
$icons_1x-sprites: sprite-map( 'icons_1x/*.png', $spacing: 2px );
$icons_2x-sprites: sprite-map( 'icons_2x/*.png', $spacing: 4px );

@mixin get-icons-sprite( $sprite, $retina: false, $svg: false ) {
  $map-url: sprite-url( $icons_1x-sprites );
  $sprite-position: sprite-position( $icons_1x-sprites, $sprite );
  background: $map-url $sprite-position no-repeat;

  $sprite-image: sprite-file( $icons_1x-sprites, $sprite );
  width: image-width( $sprite-image );
  height: image-height( $sprite-image );

  @if $retina == true {
    @include media-retina {
      background-image: sprite-url( $icons_2x-sprites );
      background-size: image-width( sprite-path( $icons_1x-sprites ) ) auto;
    }
  }
  
  // SVGのサポート
  @if $svg == true {
    .svg & {
      background-image: inline-image( '../svg/icons_svg/#{$sprite}.svg', 'image/svg+xml' );
      background-size: image-width( $sprite-image ) image-height( $sprite-image );
      background-position: 0 0;
    }
  }
}
```

## 使用例

```scss
$icon-names: (
              'icon-facebook'
              'icon-twitter'
              'icon-github'
              );

@each $icon-name in $icon-names {
  .icons-#{ $icon-name } {
    display: inline-block;
    @include get-icons-sprite( #{ $icon-name }, true, true );
  }
}
```

```html
<i class="icons-icon-facebook"></i>
<i class="icons-icon-twitter"></i>
<i class="icons-icon-github"></i>
```

生成されるCSSはこのようになります。(アイコン一つ分掲載)
SVGのデータがインラインに埋め込まれます。

```css
.icons-icon-facebook {
  display: inline-block;
  background: url('../../assets/images/icons_1x-s3241a4cef7.png') 0 0 no-repeat;
  width: 48px;
  height: 48px; }
  @media only screen and (min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3 / 2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) {
    .icons-icon-facebook {
      background-image: url('../../assets/images/icons_2x-sda4f458d0f.png');
      background-size: 48px auto; } }
  .svg .icons-icon-facebook {
    background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTYiIGhlaWdodD0iOTYiIHZpZXdCb3g9IjAgMCA5NiA5NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+aWNvbi1mYWNlYm9vazwvdGl0bGU+PHBhdGggZD0iTTY4IDIySDI2Yy0xLjY0IDAtMyAxLjM2LTMgM3Y0MmMwIDEuNjQgMS4zNiAzIDMgM2gyMi42NjRWNTEuMzQ0aC02VjQ0LjQzaDZ2LTUuOTc3YzAtNS40NiAzLjMyOC05LjExNyA5LjkxNC05LjExNyAyLjkwNiAwIDQuOTIyLjMyOCA0LjkyMi4zMjh2Ni4yMzRINTljLTEuOTkyIDAtMyAxLjEyNS0zIDMuMDk0djUuNDM4aDcuMDA4TDYyIDUxLjM0NGgtNlY3MGgxMmMxLjY0IDAgMy0xLjM2IDMtM1YyNWMwLTEuNjQtMS4zNi0zLTMtM3oiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==');
    background-size: 48px auto;
    background-position: 0 0; }
```


## SVGデータの圧縮

GruntでCompassを実行する際にSVGの圧縮プロセスを手前に挟むと良いでしょう。

[sindresorhus/grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin)

※ Gruntのワークフロー一式は近日アップ予定です。


## その他の考察

- [Image sprites or data URIs? Icon fonts or SVGs? A Grunt workflow for the ‘gold’ standard | Ben Frain](http://benfrain.com/image-sprites-data-uris-icon-fonts-v-svgs/) 

こちらで、icon-fontとSVGの比較や、data-uriによる埋め込み、sprite画像の生成について論じられています。
(以下適当訳)

- icon-fontになっているのが一番楽に管理でき、単色での着色も簡単だが、位置合わせは手間がかかる。
- SVGは複数色の着色や位置合わせが簡単だけど、素材作成に手間がかかる。
- パフォーマンス面ではdata-uriよりspriteを利用する方が優れている。
- サイズの大きなSVG SpriteはiOSでのレンダリングにバグあり? (960x560で描画されない)
- PNGの方がSVGよりもレンダリングが速い。
- spriteの自動生成をGruntで行う場合、セットアップにはかなり手間がかかる。


## まとめ

[素材の書き出しフローの検討](http://qiita.com/soheikitada/items/1ef46137ce4e7fc298fb) の時点から考えていましたが、

**SVGをdata-uriで埋め込み、PNGでfallbackする**

というのが、現時点では最もシンプルなコーディング方法である、と判断しました。