webpack-spritesmith 的一些高阶使用技巧

精灵图想必对大家来说都不陌生,就是将多个小图整合到一张图上,以此减少浏览器向服务器请求的次数,从而提高网页的加载速度。

而在 Vue 项目中,我通常会使用 webpack-spritesmith 这个 npm 包来制作需要的精灵图,而这篇文章主要是针对 webpack-spritesmith 的一些高阶使用技巧。

如果你没使用过 webpack-spritesmith ,建议熟悉了解后再阅读本篇文章。

虽然 webpack-spritesmith 已经能帮我减轻很大的工作量了,但在项目中具体使用的时候,还是会遇到一些不大不小的问题,之所以说不大不小,是因为这些问题的解决方案有很多种,而下面我所提供的解决方案,只是单纯从技术角度去思考如何处理。

痛点一:变量名冲突

在配置好各种参数,并且精灵图和对应的 .scss 文件都能生成好后,第一个痛点就来了。

我们先观察一下生成好的 .scss 文件,大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// 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;
$address-name: 'address';
$address-x: 0px;
$address-y: 0px;
$address-offset-x: 0px;
$address-offset-y: 0px;
$address-width: 100px;
$address-height: 100px;
$address-total-width: 200px;
$address-total-height: 200px;
$address-image: '~sprites.fe6c69f7e7ec270381e68e2dd4a910c1.png';
$address: (0px, 0px, 0px, 0px, 100px, 100px, 200px, 200px, '~sprites.fe6c69f7e7ec270381e68e2dd4a910c1.png', 'address', );
$feedback-name: 'feedback';
$feedback-x: 100px;
$feedback-y: 0px;
$feedback-offset-x: -100px;
$feedback-offset-y: 0px;
$feedback-width: 100px;
$feedback-height: 100px;
$feedback-total-width: 200px;
$feedback-total-height: 200px;
$feedback-image: '~sprites.fe6c69f7e7ec270381e68e2dd4a910c1.png';
$feedback: (100px, 0px, -100px, 0px, 100px, 100px, 200px, 200px, '~sprites.fe6c69f7e7ec270381e68e2dd4a910c1.png', 'feedback', );
$payment-name: 'payment';
$payment-x: 0px;
$payment-y: 100px;
$payment-offset-x: 0px;
$payment-offset-y: -100px;
$payment-width: 100px;
$payment-height: 100px;
$payment-total-width: 200px;
$payment-total-height: 200px;
$payment-image: '~sprites.fe6c69f7e7ec270381e68e2dd4a910c1.png';
$payment: (0px, 100px, 0px, -100px, 100px, 100px, 200px, 200px, '~sprites.fe6c69f7e7ec270381e68e2dd4a910c1.png', 'payment', );
$spritesheet-width: 200px;
$spritesheet-height: 200px;
$spritesheet-image: '~sprites.fe6c69f7e7ec270381e68e2dd4a910c1.png';
$spritesheet-sprites: ($address, $feedback, $payment, );
$spritesheet: (200px, 200px, '~sprites.fe6c69f7e7ec270381e68e2dd4a910c1.png', $spritesheet-sprites, );

// 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 `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);
}
}
}

其实生成出来的 .scss 文件很简单,就是包含了一些变量和 @mixin ,这样在 @import 这个 .scss 文件之后就可以很方便的进行使用了。

1
2
3
.example {
@include sprite($address);
}

但是也出现了一个问题,就是生成出来的这个变量,在使用的时候如果稍有不注意,可能就会和项目中自定义的一些全局变量重名,导致变量被覆盖。

这个问题其实可以用很简单办法解决,仔细观察就会发现,变量名是依据文件名定义的,所以在给图片文件命名的时候,定义好一个规则就行了,比如全部以 sprite- 开头,这样就极大程度避免了变量的重名的问题,只是在使用的时候会麻烦一点,就需要改成这样。

1
2
3
.example {
@include sprite($sprite-address);
}

有没有更好的方法呢?答案当然是有。在查看 webpack-spritesmith 介绍的时候看到了这句话:

Webpack plugin that converts set of images into a spritesheet and SASS/LESS/Stylus mixins, using spritesmith and spritesheet-templates.

说明 .scss 文件其实是通过模版生成的,那只要对模版修改一下,理论上就可以解决这问题了。

首先找到原始的模版,在 spritesheet-templates 这个仓库里搜寻后发现 scss.template.handlebars 这个文件。

接下来先对模版进行精简,因为生成出来的一系列变量中,只用到了这句:

1
$address: (0px, 0px, 0px, 0px, 100px, 100px, 200px, 200px, '~sprites.fe6c69f7e7ec270381e68e2dd4a910c1.png', 'address', );

它将所有的值都存放在了一个数组变量中,在 @mixin 中通过 nth() 这个方法去获取数组中其中所需要的值,所以只需要保留这段模版即可:

1
${{strings.name}}: ({{px.x}}, {{px.y}}, {{px.offset_x}}, {{px.offset_y}}, {{px.width}}, {{px.height}}, {{px.total_width}}, {{px.total_height}}, '{{{escaped_image}}}', '{{name}}', );

剩下生成 @mixin 的模版都是需要用到的,那就原封不动的复制过来,这样就初步定制了一个我的模版。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{
// Default options
'functions': true,
'variableNameTransforms': ['dasherize']
}

{{#block "sprites"}}
{{#each sprites}}
${{strings.name}}: ({{px.x}}, {{px.y}}, {{px.offset_x}}, {{px.offset_y}}, {{px.width}}, {{px.height}}, {{px.total_width}}, {{px.total_height}}, '{{{escaped_image}}}', '{{name}}');
{{/each}}

{{#block "sprite-functions"}}
{{#if options.functions}}
@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);
}
{{/if}}
{{/block}}

接下来要解决变量名冲突的问题,我在模版里给变量名增加一个前缀,也叫 sprite-

1
$sprite-{{strings.name}}: ({{px.x}}, {{px.y}}, {{px.offset_x}}, {{px.offset_y}}, {{px.width}}, {{px.height}}, {{px.total_width}}, {{px.total_height}}, '{{{escaped_image}}}', '{{name}}');

这样就好了么?还没有。虽然变量名不冲突了,但在使用的时候还是没有变化。

1
2
3
.example {
@include sprite($sprite-address);
}

依旧要多写 sprite- 这个前缀,为了偷这个懒,我进行了进一步的定制。

因为 sprite 这个 @mixin 需要传入指定的变量做为参数,那能不能改成传入一个字符串,动态拼装一个变量呢?比如这样:

1
2
3
4
5
6
@mixin sprite($name) {
@include sprite-image($sprite-#{$name});
@include sprite-position($sprite-#{$name});
@include sprite-width($sprite-#{$name});
@include sprite-height($sprite-#{$name});
}

很遗憾,并不行,查了资料才知道 SASS 并不支持动态创建引用变量。虽然不支持动态变量,那就换个思路,把所需要的变量放到一个数组变量里总可以吧?这样说可能有点绕,直接看代码吧。

1
2
3
4
5
6
7
8
9
10
11
12
$sprites: (
{{#each sprites}}
{{strings.name}}: $sprite-{{strings.name}},
{{/each}}
);

@mixin sprite($name) {
@include sprite-image(map-get($sprite, #{$name}));
@include sprite-position(map-get($sprite, #{$name}));
@include sprite-width(map-get($sprite, #{$name}));
@include sprite-height(map-get($sprite, #{$name}));
}

我单独定义了一个 $sprites 变量,里面是一个键值对的数组,键是文件名,值是通过拼装出来变量。而在 @mixin 中则使用了 map-get() 这个方法去获取到对应的变量,再接下来就是和原先一样了。

这样处理之后,直接就可以这样使用了,甚至比原有的方式更简单,都不需要写 $ 符号了。

1
2
3
.example {
@include sprite(address);
}

最后就是如何调用我的自定义模版了,这在 webpack-spritesmith 的文档中详细的介绍,我就不具体细说了,贴一份大致的代码吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, 'src/assets/sprites'),
glob: '*.png'
},
target: {
image: path.resolve(__dirname, 'src/assets/images/sprites.[hash].png'),
css: [
[path.resolve(__dirname, 'src/assets/style/sprites.scss'), {
format: 'handlebars_based_template'
}]
]
},
customTemplates: {
'handlebars_based_template': path.resolve(__dirname, 'my.scss.template.handlebars')
},
// 样式文件中调用雪碧图地址写法
apiOptions: {
cssImageRef: '~sprites.[hash].png'
}
})

痛点二:文件名冲突

在说这个问题之前,先说明一下, webpack-spritesmith 是可以生成多张精灵图的,虽然办法有点蠢,就是多增加一个 new SpritesmithPlugin() 实例,把里面的路径都替换即可。

当然办法是蠢的,但我可是聪明的,可以借助 fs 模块遍历精灵图存放目录的文件夹,依次执行 new SpritesmithPlugin() 就行啦,具体可点击看这里

如果项目比较大,并且有多个模块,这个时候可能就会需要根据不同模块生成对应的精灵图,也就是小图会归类到不同的文件夹,但也就出现一个情况,不同文件夹里的图片文件名可能会重名,比如这种情况:

1
2
3
4
5
6
7
sprites
├─ order
│ ├─ success.png
│ └─ close.png
└─ payment
├─ success.png
└─ fail.png

sprites 目录下有两个文件夹,分别是 order 和 payment ,并且里面都有一个 success.png 文件,假设这两个目录生成出的两个 .scss 文件需要在同一个页面上调用,就又出现上面变量名冲突的问题了。

举例的这种情况可能不常见,更多情况可能是有一个存放全局精灵图的目录,里面的文件名可能会和某个模块文件夹下的文件名重名,导致在使用的时候出现变量名冲突。

这个问题也可以用简单的办法解决,不就是改个文件名嘛,但如果项目是多人协作开发,这个问题就会变成一个潜在隐患,不知道什么时候就和别人定义的名字重名了。所以我还是决定用技术手段来解决。

方案一

我最先的思路是,能不能将文件夹的名字做为一个新增变量注入到模版中,这样在模板中就可以通过模版变量 {{...}} 的方式去使用了,但翻遍了 webpack-spritesmith 文档也没找到相关的 API ,不过倒是在模版选项里看到了 spritesheetName 的设置项,它对应的模版变量是 spritesheet_info.name ,这是一个没被使用到模版变量,正好可以用来解决我的问题。

使用也很简单,只需要在配置里增加这一项设置,并设置为文件夹的名字即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
new SpritesmithPlugin({
...
target: {
image: path.resolve(__dirname, 'src/assets/images/order.[hash].png'),
css: [
[path.resolve(__dirname, 'src/assets/style/order.scss'), {
spritesheetName: 'order',
format: 'handlebars_based_template'
}]
]
},
...
})

接下来在模版里通过 {{spritesheet_info.strings.name}} 就可以得到刚才设置的值,只需要把对应位置的变量和 @mixin 都加上这个前缀就行了,需要注意的是,在 {{#each}} 里需要通过增加 ../ 的方式来访问父级属性,下面为部分模版代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
${{spritesheet_info.strings.name}}-sprites: (
{{#each sprites}}
{{strings.name}}: ${{../spritesheet_info.strings.name}}-sprite-{{strings.name}},
{{/each}}
);

@mixin {{spritesheet_info.strings.name}}-sprite($name) {
@include {{spritesheet_info.strings.name}}-sprite-image(map-get(${{spritesheet_info.strings.name}}-sprites, #{$name}));
@include {{spritesheet_info.strings.name}}-sprite-position(map-get(${{spritesheet_info.strings.name}}-sprites, #{$name}));
@include {{spritesheet_info.strings.name}}-sprite-width(map-get(${{spritesheet_info.strings.name}}-sprites, #{$name}));
@include {{spritesheet_info.strings.name}}-sprite-height(map-get(${{spritesheet_info.strings.name}}-sprites, #{$name}));
@include {{spritesheet_info.strings.name}}-sprite-size(map-get(${{spritesheet_info.strings.name}}-sprites, #{$name}));
}

这样修改后,生成出来的 .scss 文件中的变量和 @mixin 都加上了文件夹名做为前缀,所以在使用上也稍微有变化。如果有用过 Compass 制作精灵图的,一定不陌生。

1
2
3
.example {
@include order-sprite(success);
}

方案二

这个思路是在看 webpack-spritesmith 文档时想到的,然后结合了上网的一些参考资料最终实现的。首先先看一下文档里对 generateSpriteName() 这个方法的介绍吧。

generateSpriteName - function. Takes full path to source image file and expected to return name by which it will be referenced in API. Return value will be used as sprite.name for spritesheet-templates. Default behaviour is to use filename (without dirname and extension)

大概意思是说,通过 generateSpriteName() 方法可以获取到源文件的完整路径,并且可以手动返回一个将在 API 中引用的名称,默认返回的是文件名。

既然能得到完整路径,那在返回的时候,把文件夹的名称和文件名一起返回,这样在模版中不就解决变量冲突的问题了么,毕竟文件夹名加上文件名,这样组出来的名称能重名的就很少了。

直接看配置代码吧,这些需要用到 Node 里的 path 模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
new SpritesmithPlugin({
...
apiOptions: {
generateSpriteName: function(fullPath) {
let parsed = path.parse(fullPath)
let dir = parsed.dir.split(path.sep)
let moduleName = dir[dir.length - 1]
return moduleName + '_' + parsed.name
},
cssImageRef: '~order.[hash].png'
},
...
})

打开生成出来的 .scss 文件看看。

1
$sprite-order-success: (0px, 0px, 0px, 0px, 100px, 100px, 200px, 200px, '~order.fe6c69f7e7ec270381e68e2dd4a910c1.png', 'order_success');

那么如何使用呢?

1
2
3
.example {
@include sprite(order-success);
}

是不是觉得和方案一没太大差别,其实在使用上确实没太大差别。两个方案生成的变量名都是唯一的,只不过前者是从 @mixin 命名上做了区分,不同的文件夹会生成多个不同的 @mixin ;而后者会生成多个同名且代码一模一样的 @mixin ,在使用的时候,后面定义的同名 @mixin 会进行重写覆盖,但因为代码是一样的,所以使用上并无影响。

但从代码优雅上来说,还是方案一更好,各自的变量和 @mixin 都相对独立,不受干扰。

痛点三:响应式布局

因为生成的 .scss 文件里使用的都是 px 单位,而在响应式布局下会使用 rem 或者 vw/vh 做为长度大小单位,这时候就出现一个问题,如何在响应式布局下更方便的使用生成出来的 .scss 文件呢?

这里我以 vw/vh 举例,首先先给项目安装 postcss-px-to-viewport 这个 npm 包,它可以很方便的帮我把 px 转成 vw/vh 。

这个包如何使用这里不做更多介绍,重点是在配置好后, .scss 文件定义的一些 px 单位也能正常转换成 vw 单位了。但是因为 vw 是一个相对长度单位,在不同终端下它计算出来的实际长度并不是一致的,而生成出来的精灵图文件的尺寸是固定的,导致 background-position 定位不能准确的定位到某个图片上。

要解决这个问题,就需要使用 background-size 这个属性了,但不是使用 cover/contain 这两个预设的值,因为 cover 是拉伸图片使之充满元素,虽然元素被铺满了,但是图片有可能显示不全;而 contain 刚好相反,它是拉伸图片使图片完全显示在元素内,虽然图片显示全了,但是元素可能不会被铺满。所以这里就需要手动设置背景图的宽高来解决。思路确定了,那就看看模版里有没有提供合并后图片宽高的参数。

1
$sprite-{{strings.name}}: ({{px.x}}, {{px.y}}, {{px.offset_x}}, {{px.offset_y}}, {{px.width}}, {{px.height}}, {{px.total_width}}, {{px.total_height}}, '{{{escaped_image}}}', '{{name}}');

从数组变量中可以看到 {{px.total_width}}{{px.total_height}} 这两个就是精灵图图片的宽高了,有了这两个值,就可以在 @mixin 中增加一句 background-size 的样式代码了,为了代码风格统一,我增加一个 @mixin sprite-size

1
2
3
4
5
6
7
8
9
10
11
@mixin sprite-size($sprite) {
background-size: nth($sprite, 7) nth($sprite, 8);
}

@mixin sprite($name) {
@include sprite-image(map-get($sprites, #{$name}));
@include sprite-position(map-get($sprites, #{$name}));
@include sprite-size(map-get($sprites, #{$name}));
@include sprite-width(map-get($sprites, #{$name}));
@include sprite-height(map-get($sprites, #{$name}));
}

这样 background-size 设置的宽高就和精灵图的实际宽高一致了,再通过 px 转 vw 后,也不会出现任何问题。

另外需要注意的是,因为 vw 计算出来的实际长度大概率会出现小数,如果精灵图贴合太紧凑,就会出现图片显示不全或超出的情况,解决这个问题只需要在配置中增加 padding 的设置即可。

1
2
3
4
5
6
7
new SpritesmithPlugin({
...
spritesmithOptions: {
padding: 10
},
...
})

补充痛点:嫌手动引入麻烦

最后这个痛点是因第二个痛点引发而来的,因为项目模块多,生成的 .scss 文件也很多,在页面上使用的时候,经常会忘记要先引入对应的 .scss 文件后,再去使用 @mixin

因为生成的都是变量和 @mixin ,所以我就在想能不能全局引用,这样就不用在每个页面上使用的时候再去引用了。但是手动一个个在 main.js 里引用还是不够智能,于是网上搜寻了一番,发现了 sass-resources-loader 这个 npm 包。

This loader will @import your SASS resources into every required SASS module. So you can use your shared variables & mixins across all SASS styles without manually importing them in each file. Made to work with CSS Modules!

官方文档里第一句话就说的很清楚了。因为没啥技术含量,下面接直接贴配置代码了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
chainWebpack: config => {
const oneOfsMap = config.module.rule('scss').oneOfs.store
oneOfsMap.forEach(item => {
item.use('sass-resources-loader')
.loader('sass-resources-loader')
.options({
resources: [
'./src/assets/styles/global/*.scss',
'./src/assets/sprites/*.scss'
]
})
.end()
})
}

这里我还指定一个 global 目录下的所有 .scss 文件,这是我存放整站自定义的一些全局变量、@mixin 等一些资源。

需要注意的是, sass-resources-loader 只会把变量、@mixin@function 这类资源注入到全局,如果文件里包含实际的样式代码,它并不会生效。

总结

也没啥可总结的,该说的在上面都说了,非要总结的话,那就是多看官方文档,很多问题其实插件/包的作者都有考虑到,合理使用就能达到想要的效果。

最后分享下完整的配置和模版,因为上面都是代码片段,我知道很多人都偷懒,想直接用现成的。

vue.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const path = require('path')
const SpritesmithPlugin = require('webpack-spritesmith')

module.exports = {
publicPath: '',
configureWebpack: {
resolve: {
modules: ['node_modules', 'assets/sprites']
},
plugins: [
new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, 'src/assets/sprites/example'),
glob: '*.png'
},
target: {
image: path.resolve(__dirname, 'src/assets/sprites/example.[hash].png'),
css: [
[path.resolve(__dirname, 'src/assets/sprites/example.scss'), {
format: 'handlebars_based_template',
spritesheetName: 'example'
}]
]
},
customTemplates: {
'handlebars_based_template': path.resolve(__dirname, 'my.scss.template.handlebars')
},
apiOptions: {
cssImageRef: '~example.[hash].png'
},
spritesmithOptions: {
algorithm: 'binary-tree',
padding: 10
}
})
]
}
}

my.scss.template.handlebars

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
{
// Default options
'functions': true,
'variableNameTransforms': ['dasherize']
}

{{#block "sprites"}}
{{#each sprites}}
${{../spritesheet_info.strings.name}}-sprite-{{strings.name}}: ({{px.x}}, {{px.y}}, {{px.offset_x}}, {{px.offset_y}}, {{px.width}}, {{px.height}}, {{px.total_width}}, {{px.total_height}}, '{{{escaped_image}}}', '{{name}}');
{{/each}}

${{spritesheet_info.strings.name}}-sprites: (
{{#each sprites}}
{{strings.name}}: ${{../spritesheet_info.strings.name}}-sprite-{{strings.name}},
{{/each}}
);
{{/block}}

{{#block "sprite-functions"}}
{{#if options.functions}}
@mixin {{spritesheet_info.strings.name}}-sprite-width($sprite) {
width: nth($sprite, 5);
}

@mixin {{spritesheet_info.strings.name}}-sprite-height($sprite) {
height: nth($sprite, 6);
}

@mixin {{spritesheet_info.strings.name}}-sprite-position($sprite) {
$sprite-offset-x: nth($sprite, 3);
$sprite-offset-y: nth($sprite, 4);
background-position: $sprite-offset-x $sprite-offset-y;
}

@mixin {{spritesheet_info.strings.name}}-sprite-size($sprite) {
background-size: nth($sprite, 7) nth($sprite, 8);
}

@mixin {{spritesheet_info.strings.name}}-sprite-image($sprite) {
$sprite-image: nth($sprite, 9);
background-image: url(#{$sprite-image});
}

@mixin {{spritesheet_info.strings.name}}-sprite($name) {
@include {{spritesheet_info.strings.name}}-sprite-image(map-get(${{spritesheet_info.strings.name}}-sprites, #{$name}));
@include {{spritesheet_info.strings.name}}-sprite-position(map-get(${{spritesheet_info.strings.name}}-sprites, #{$name}));
@include {{spritesheet_info.strings.name}}-sprite-size(map-get(${{spritesheet_info.strings.name}}-sprites, #{$name}));
@include {{spritesheet_info.strings.name}}-sprite-width(map-get(${{spritesheet_info.strings.name}}-sprites, #{$name}));
@include {{spritesheet_info.strings.name}}-sprite-height(map-get(${{spritesheet_info.strings.name}}-sprites, #{$name}));
}
{{/if}}
{{/block}}

{{#block "spritesheet-functions"}}
{{#if options.functions}}
@mixin {{spritesheet_info.strings.name}}-sprites($sprites) {
@each $sprite in $sprites {
$sprite-name: nth($sprite, 10);
.sprite-#{$sprite-name} {
@include {{spritesheet_info.strings.name}}-sprite($sprite);
}
}
}
{{/if}}
{{/block}}

最后的最后,本篇文章涉及到的内容也已经同步更新到 vue-autumation 中,这是一个基于 Vue CLI 3 制作的 Vue 脚手架,能方便快速进行业务开发。

参考