9.Icon sprite与gulp-svg-sprite插件

为了避免在加载网页组件的过程中,因为页面中过多的小图标而导致过多的HTTP请求,我们往往会通过使用icon sprite的方式只加载一张合成了所有小图标的大“雪碧图”,然后通过background属性以背景图的形式引入,并结合background-position以及width、height等CSS属性来使用它。如果纯手动的话,这将是一个冗长乏味的过程,因为我们需要自行精确控制图标的位置,而gulp-svg-sprite插件将能够为我们自动生成相应的CSS代码(https://github.com/jkphl/svg-sprite):

npm install gulp-svg-sprite --save-dev

然后创建sprite.js文件并在gulpfile.js文件中引入:require("./gulp/tasks/sprite.js");。接下来就可以自定义任务并使用该插件了,以下是sprite.js文件中的代码:

var gulp = require("gulp"),
svgSprite = require("gulp-svg-sprite");

var config = {
    mode: {
        css: {
            render: {
                css: {
                    //指定生成对应雪碧图中小图标CSS文件的模板文件的路径
					template: "./gulp/templates/sprite.css"
                }
            }
        }
    }
};

gulp.task("createSprite",function(){
    return gulp.src("./app/assets/images/icons/**/*.svg")
            .pipe(svgSprite(config))
            .pipe(gulp.dest("./app/temp/sprite/"));
});

这样该插件会自动将“./app/assets/images/icons/**/*.svg” 路径下的所有svg icon小图标合成为指定路径(”./app/temp/sprite/css/svg/”)下的一张雪碧图。并根据模板文件中的参数自动为每一个小图标去生成对应的CSS样式文件。模板文件(”./gulp/templates/sprite.css”)中的代码如下:

{{#shapes}}
    .icon--{{base}} {
        width: {{width.outer}}px;
        height: {{height.outer}}px;
        background-image: url("/temp/sprite/css/{{{sprite}}}");
        background-position: {{position.relative.xy}};
    }
{{/shapes}}

其中每个参数的具体含义可以到相应的github文档上查阅。最终在”./app/temp/sprite/css/sprite.css”的sprite.css文件中生成的各小图标的css样式如下:

 .icon--clear-view-escapes {
        width: 142.4px;
        height: 59.53px;
        background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        background-position: 0 0;
    }
    .icon--comment {
        width: 64px;
        height: 64px;
        background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        background-position: 99.70443349753694% 0;
    }
    .icon--facebook {
        width: 21.23px;
        height: 42.01px;
        background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        background-position: 26.040607071652357% 45.795830448496034%;
    }
    .icon--fire {
        width: 56px;
        height: 64px;
        background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        background-position: 0 99.56481481481481%;
    }
    .icon--globe {
        width: 64px;
        height: 64px;
        background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        background-position: 27.586206896551722% 99.56481481481481%;
    }
    .icon--instagram {
        width: 42.01px;
        height: 42.01px;
        background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        background-position: 63.2917018534157% 49.234556504346486%;
    }
    .icon--mail {
        width: 64px;
        height: 48px;
        background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        background-position: 0 48.00806451612903%;
    }
    .icon--rain {
        width: 64px;
        height: 64px;
        background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        background-position: 99.70443349753694% 59.25925925925926%;
    }
    .icon--star {
        width: 64px;
        height: 64px;
        background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        background-position: 59.11330049261084% 99.56481481481481%;
    }
    .icon--twitter {
        width: 51.78px;
        height: 42.06px;
        background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        background-position: 94.04330452560171% 98.50700323226104%;
    }
    .icon--wifi {
        width: 60px;
        height: 64px;
        background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        background-position: 68.79227053140097% 0;
    }
    .icon--youtube {
        width: 34.75px;
        height: 42px;
        background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        background-position: 36.69752421959096% 45.792307692307695%;
    }

接下来,我们就可以在项目中轻松的使用这些样式文件来引入雪碧图中对应的小图标了(可别忘了把复制过来的样式文件通过@import引入到主样式文件中),非常的方便。

最后还有一个步骤,如果我们希望能够将生成的雪碧图样式文件复制到我们项目中存放样式文件的指定路径中,gulp.task()同样能够帮助我们完成这项工作,我们可以在原先的sprite.js文件中继续添加如下代码:


gulp.task("copySpriteCSS",function(){
    return gulp.src("./app/temp/sprite/css/*.css")
            .pipe(gulp.dest("./app/assets/styles/modules"));
});

毫无疑问,gulp.src()中指定的生成的雪碧图样式文件的路径,而gulp.dest()中指定的是你所要导入该文件的项目路径。最后在命令行中键入:gulp copySpriteCSS就OK了。

但是,细心的朋友会发现,这样每当我想要生成并复制雪碧图样式文件时,就必须得在命令行中依次键入gulp createSprite以及copySpriteCSS命令,岂不麻烦?于是,我们可以继续在sprite.js文件中将这两个任务合并为一个任务,从而只用一个命令:

gulp.task("icons",["createSprite", "copySpriteCSS"]);

这里我们巧妙的利用[“createSprite”, “copySpriteCSS”]数组列表中的任务,将作为任务icons的依赖这一特点,也就是说,在执行icons任务时,必须得先执行createSprite和copySpriteCSS任务。

但是这里还有个问题,就是依赖中的任务是同时执行的,这就意味着createSprite和copySpriteCSS任务将会同时执行,这不是我们想要的结果,因为copySpriteCSS任务必须在createSprite任务完成之后才能正常的执行。这里有一个小技巧,就是将createSprite任务作为copySpriteCSS任务的依赖,这样就形成了执行任务链的先后顺序。

完整代码如下:

gulp.task("createSprite",function(){
    return gulp.src("./app/assets/images/icons/**/*.svg")
            .pipe(svgSprite(config))
            .pipe(gulp.dest("./app/temp/sprite/"));
});

gulp.task("copySpriteCSS", ["createSprite"], function(){
    return gulp.src("./app/temp/sprite/css/*.css")
            .pipe(rename("_sprite.css"))
            .pipe(gulp.dest("./app/assets/styles/modules"));
});

gulp.task("icons",["createSprite", "copySpriteCSS"]);

最后只要gulp icons就能一步到位~


题外话:gulp-rename插件

如果我们在复制文件的过程中希望对复制文件进行改名,就可以使用gulp-rename插件。比如,在上面的例子中,在我们项目中的CSS样式为了更加的organization,都是使用_开头的,而如果直接复制的话,复制后的雪碧图样式文件是原来的名字,所以我们需要通过重命名插件,在管道化复制文件的过程中对该样式文件进行重命名。同样,先用NPM安装该插件:

npm install gulp-rename --save-dev

然后依旧是sprite.js文件中,先引入该插件:

var rename = require("gulp-rename");

接着再使用:

gulp.task("copySpriteCSS",function(){
    return gulp.src("./app/temp/sprite/css/*.css")
            .pipe(rename("_sprite.css"))
            .pipe(gulp.dest("./app/assets/styles/modules"));
});


回到前面的话题,生成的雪碧图样式文件具体如何使用可以看下面这个例子:

假如我们项目中原来的图片是直接通过img标签的方式引入的:

<h2 class="section-title">
	<img class="section-title__icon" src="assets/images/icons/star.svg">Our <strong>Features</strong>
</h2>

然后我们可以使用span标签替换该img标签,并使用对应的小图标的类名即可:

<h2 class="section-title">
	<span class="icon--star section-title__icon"></span>Our <strong>Features</strong>
</h2>

其中的icon–star就是该小图标对应的CSS类。

优化:以上任务能够进一步优化:

(1)优化点一:生成的雪碧图样式文件中,每一个小图标都重复了同样的CSS语句,即background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");,这是没有必要的。

具体优化方法:此处的优化需要改动模板中的内容,{{#shapes}}{{/shape}}语法中的内容会自动生成在每一个雪碧图中的小图标对应的样式类中,所以我们需要把background-image: url("/temp/sprite/css/{{{sprite}}}");提取出来。把它放到{{#first}}{{/first}}语法中,该语法中的内容将只会生成一次,并放置在生成的样式文件中。为了能够以类的形式单独引入,我们将其声明为icon类,具体代码如下:

{{#shapes}}
    {{#first}}
        .icon {
            background-image: url("/temp/sprite/css/{{{sprite}}}");
        }
    {{/first}}

    .icon--{{base}} {
        width: {{width.outer}}px;
        height: {{height.outer}}px;
        background-position: {{position.relative.xy}};
    }
{{/shapes}}

重新运行gulp icons命令,生成的样式文件如下:

    .icon {
            background-image: url("/temp/sprite/css/svg/sprite.css-69f19c2e.svg");
        }

    .icon--clear-view-escapes {
        width: 142.4px;
        height: 59.53px;
        background-position: 0 0;
    }

    .icon--comment {
        width: 64px;
        height: 64px;
        background-position: 99.70443349753694% 0;
    }
	......

此后我们只需要在添加对应小图标类时,额外添加一个icon类去指定引用的雪碧图路径就OK了。

<h2 class="section-title">
	<span class="icon icon--star section-title__icon"></span>Our <strong>Features</strong>
</h2>

(2)优化点二:在模板中添加一个注释,该注释也会自动出现在生成的样式文件中

/* Do not edit modules/_sprite directly as it is generated automaticall by Gulp.
Inserted edit gulp/templates/sprite*/

例如可以添加如上注释,告知其他开发者如果需要新增或修改雪碧图中的内容,不要直接在生成的样式文件中进行修改,应该去修改模板文件或者是sprit.js文件中的内容。修改完毕后,再通过自动生成来得到想要的样式文件。

所以记住,注释也是十分重要的。

(3)优化点三:简化生成的雪碧图的名字以及将生成的雪碧图导入到项目文件中:

这一点可能无关紧要,即使使用生成的默认雪碧图的名字不会对项目有任何的影响(sprite.css-69f19c2e.svg),但是雪碧图名字中的“.css”也是不必要的存在。或许我们希望能够定义更为简洁的雪碧图名字,如果你觉得有必要那么可以继续看下去:

此时,我们需要修改sprite.js文件中的config对象,在mode对象下的CSS对象中新增一个sprite属性:

var config = {
    mode: {
        css: {
            sprite: "svg/sprite.svg",
            render: {
                css: {
                    template: "./gulp/templates/sprite.css"
                }
            }
        }
    }
};

运行命令gulp icons后结果如下:

 .icon {
            background-image: url("/temp/sprite/css/svg/sprite-69f19c2e.svg");
        }

雪碧图的名字更为简洁了。

接下来,还可以将生成的雪碧图复制导入到项目文件中,当然了此时也需要去修改模板的引用路径。

首先是sprite.js文件中,新增一个任务copySpriteGraphic,修改后的完整文件如下:

var gulp = require("gulp"),
svgSprite = require("gulp-svg-sprite"),
rename = require("gulp-rename");

var config = {
    mode: {
        css: {
            sprite: "svg/sprite.svg",
            render: {
                css: {
                    template: "./gulp/templates/sprite.css"
                }
            }
        }
    }
};

gulp.task("createSprite",function(){
    return gulp.src("./app/assets/images/icons/**/*.svg")
            .pipe(svgSprite(config))
            .pipe(gulp.dest("./app/temp/sprite/"));
});

gulp.task("copySpriteGraphic", ["createSprite"],function(){
    return gulp.src("./app/temp/sprite/css/**/*.svg")
        .pipe(gulp.dest("./app/assets/images/sprites"));
});

gulp.task("copySpriteCSS", ["createSprite"], function(){
    return gulp.src("./app/temp/sprite/css/*.css")
            .pipe(rename("_sprite.css"))
            .pipe(gulp.dest("./app/assets/styles/modules"));
});

gulp.task("icons",["createSprite", "copySpriteGraphic", "copySpriteCSS"]);

接下来再修改模板文件,主要是修改引入路径:

{{#first}}
        .icon {
            background-image: url("/assets/images/sprites/{{{sprite}}}");
        }
{{/first}}

重新运行gulp icons命令就完成了。

(4)优化点四:在每次修改icon(新增或减少)后自动生成雪碧图及对应样式文件前,可以对旧得雪碧图和样式文件进行删除。

若要删除某个文件,此时需要用到一个del插件,首先通过NPM下载下来:

npm install del --save-dev

同样通过var del = require("del")的方式引入。并新增一个任务beginClean,完整文件代码如下:

var gulp = require("gulp"),
svgSprite = require("gulp-svg-sprite"),
rename = require("gulp-rename"),
del = require("del");

var config = {
    mode: {
        css: {
            sprite: "svg/sprite.svg",
            render: {
                css: {
                    template: "./gulp/templates/sprite.css"
                }
            }
        }
    }
};

gulp.task("beginClean",function(){ 
    //数组中是要删除文件的路径,我们第一个要删除的temp中原有的雪碧图及样式文件,另一个是原先复制到项目路径中雪碧图及样式文件。
	return del(["./app/temp/sprite", "./app/assets/images/sprites"]);
});

gulp.task("createSprite", ["beginClean"], function(){
    return gulp.src("./app/assets/images/icons/**/*.svg")
            .pipe(svgSprite(config))
            .pipe(gulp.dest("./app/temp/sprite/"));
});

gulp.task("copySpriteGraphic", ["createSprite"],function(){
    return gulp.src("./app/temp/sprite/css/**/*.svg")
        .pipe(gulp.dest("./app/assets/images/sprites"));
});

gulp.task("copySpriteCSS", ["createSprite"], function(){
    return gulp.src("./app/temp/sprite/css/*.css")
            .pipe(rename("_sprite.css"))
            .pipe(gulp.dest("./app/assets/styles/modules"));
});

gulp.task("icons",["beginClean", "createSprite", "copySpriteGraphic", "copySpriteCSS"]);

之后运行gulp icons,以后每次更改图标库后,重新生成的文件都会是最新的。不存在旧雪碧图文件。

发表评论

电子邮件地址不会被公开。 必填项已用*标注