gulpでsvgスプライト自動生成


gulp環境でこれまでcsvスプライトを自動生成させていたのですが、svgスプライトへ全て切り替えてしまおうと思って作業した際の備忘録です。

やること

1.SVGを指定のディレクトリに入れたら自動でその中の全てのSVGをまとめて一つのSVGファイルを作成
2.SVGのポジションも自動で生成

HTTP/2だとスプライトはあまり効率が良くないとのことなので今後実用的かどうかは微妙なところです。

SVGスプライトのソースコピー元です。:https://www.liquidlight.co.uk/blog/article/creating-svg-sprites-using-gulp-and-sass/

前提

・gulpが実行できる環境

1.SVGを指定のディレクトリに入れたら自動でその中の全てのSVGをまとめて一つのSVGファイルを作成

①必要なモジュールをインストール

npm install gulp gulp-sass browser-sync gulp-plumber gulp-notify gulp-size gulp-svg-sprite gulp-util --save-dev

②_sprite-template.scssの作成
_sprite-template.scss

$icons: (
      sprite: (width: {{spriteWidth}}px, height: {{spriteHeight}}px, svgPath: '../img/sprite.svg'),
{{#shapes}}
    {{base}}: (width: {{width.inner}}px, height: {{height.inner}}px, backgroundX: {{position.absolute.x}}px, backgroundY: {{position.absolute.y}}px),
{{/shapes}});

②gulpfile.jsを編集
ディレクトリのパスは各環境で置き換える。
関係あるところだけ抜粋

var gulp = require('gulp'); 
var sass = require('gulp-sass');
var browser = require("browser-sync").create();
var plumber = require("gulp-plumber");
var notify = require('gulp-notify'); 
var $ = {
    gutil: require('gulp-util'),
    svgSprite: require('gulp-svg-sprite'),
    size: require('gulp-size'),
}

//ブラウザ確認用
gulp.task("server", function() {
    browser.init({
        server: "./",
        port: 8888
    });
});

//sass実行&CSS圧縮
gulp.task('sass', function() {
    gulp.src('sass/**/*scss')
        .pipe(plumber())
        .pipe(sass({
            outputStyle: 'compact',
        }).on('error', sass.logError))
        .pipe(autoprefixer())
        .pipe(gulp.dest('src/css/'))
        .pipe(browser.reload({stream:true}))
        .pipe(notify({
            title: 'Sassをコンパイルしました。',
            message: new Date()
        }));
});

//SVGスプライト
gulp.task('svgSprite', function () {
    return gulp.src("src/img/svg/*.svg")
        .pipe($.svgSprite({
            shape: {
                spacing: {
                    padding: 5
                }
            },
            mode: {
                css: {
                    dest: "./",
                    layout: "diagonal",
                    sprite: "./sprite.svg",
                    bust: false,
                    render: {
                        scss: {
                            dest: "sass/_svgSprite.scss",
                            template: "sass/_sprite-template.scss"
                        }
                    }
                }
            },
            variables: {
                mapname: "icons"
            }
        }))
        .pipe(gulp.dest("src/img/"));
});

gulp.task("default",['server'], function() {
    gulp.watch("sass/**/*.scss",["sass"]);
    gulp.watch('src/img/svg/*.svg', ['svgSprite']);
});

2.指定するCSSも自動で生成

①svgのmixinファイルを作成
_svgMixins.scss

////
/// @author Mike Street
/// @group Sprite
////

/**
 * The following variable and function originate from the sass-mq library.
 * If you have already included it, you can eliminate the below
 * https://github.com/sass-mq/sass-mq/blob/master/_mq.scss
 */

/// Base font size on the `<body>` element
/// @type Number (unit)
$mq-base-font-size: 16px !default;

/// Convert pixels to ems
///
/// @param {Number} $px - value to convert
/// @param {Number} $base-font-size ($mq-base-font-size) - `<body>` font size
///
/// @example scss
///  $font-size-in-ems: mq-px2em(16px);
///  p { font-size: mq-px2em(16px); }
///
/// @requires $mq-base-font-size
/// @returns {Number}
@function mq-px2em($px, $base-font-size: $mq-base-font-size) {
    @if unitless($px) {
        @warn "Assuming #{$px} to be in pixels, attempting to convert it into pixels.";
        @return mq-px2em($px + 0px); // That may fail.
    } @else if unit($px) == em {
        @return $px;
    }
    @return ($px / $base-font-size) * 1em;
}


/// Set the `$sprite` map
/// @group sprite
$sprite: map-get($icons, sprite) !default;

/// Retrive an attributes value for a specific icon in the sprite map
/// @param {string} $icon - The name of the icon
/// @param {string} $attr - The attribute you wish to retrieve (e.g. width)
@function sprite-attr($icon, $attr) {
  $newIcon: map-get($icons, $icon);
  @if $newIcon == null {
    @warn "Can't find an icon with the name #{$icon}";
  }
  @return map-get($newIcon, $attr);
}

/// Create a map with the specified icon of attributes
/// @param {string} $icon - The name of the icon
@function icon-attr($icon) {
  $attr: (
    width: sprite-attr($icon, width),
    height: sprite-attr($icon, height),
    x: sprite-attr($icon, backgroundX),
    y: sprite-attr($icon, backgroundY)
  );

  @return $attr;
}

/// Get the width of an icon in em
/// @param {string} $icon - The name of the icon
@function icon_width($icon) {
  @return mq-px2em(sprite-attr($icon, width));
}

/// Get the height of an icon in em
/// @param {string} $icon - The name of the icon
@function icon_height($icon) {
  @return mq-px2em(sprite-attr($icon, height));
}

/// Assign the correct SVG background image and dimensions to the element
%sprite {
  display: inline-block;
  background-image: url(map-get($sprite, svgPath));
  background-size: mq-px2em(map-get($sprite, width)) mq-px2em(map-get($sprite, height));
}


/// Add an SVG sprite icon using em positioning
/// @param {string} $icon - The name of the icon
/// @param {string} $type [all] - The properties wanted (can be `all`, `size` or `bg`).
/// - `all` ensures the icon has the background, position and size.
/// - `size` is just for dimensions
/// - `bg` just  applies the backgrounds to an element
/// @example scss - Usage
/// .class {
///   @include sprite(arrow);
///   &:hover {
///     @include sprite(arrowDown, bg)
///   }
/// }
///
/// @example css - Output
/// .class {
///   display: inline-block;
///   background-image: url("../img/sprite.svg");
///   background-size: 34.25em 32.1875em;
/// }
///
///
/// .class {
///   width: 1.3125em;
///   height: 1.3125em;
///   background-position: -0.3125em -0.3125em;
/// }
/// .class:hover {
///   background-position: -2.25em -2.25em;
/// }
@mixin sprite($icon, $type: all) {
  @if $type == all {
    // Shares the backgrounds
    @extend %sprite;
  }

  $iconMap: icon-attr($icon);

  // Outputs dimensions in em
  @if $type == all or $type == size {
    width: mq-px2em(map-get($iconMap, width) + 1);
    height: mq-px2em(map-get($iconMap, height) + 1);
  }

  // Outputs background position in em
  @if $type == all or $type == bg {
    background-position: mq-px2em(map-get($iconMap, x) - 5) mq-px2em(map-get($iconMap, y) - 5);
  }
}

②SVGの画像を指定するファイルを作成
svg.scss

@import "svgSprite";

$sprite: map-get($icons, sprite) !default;
$baseFontSize: 16px !default;

@import 'svgMixins';

.クラス名 {
  &:before {
    @include sprite(画像名);
    content: '';
  }
}

③HTML側にCSSを読み込んで指定したクラス名を記述

<link rel="stylesheet" href="css/svg.css">

<i class="クラス名"></i>

④表示されてればOK

出力されるSVGは2300px x 2300px を超えないように注意

こちらのサイトを見たほうが参考になります。
https://www.liquidlight.co.uk/blog/article/creating-svg-sprites-using-gulp-and-sass/