前端图片二三事
前言:在我刚刚上大学的时候,也就是 17 的时候,以这个时间点为基准,再把时间往前推一点,那时候的前端是怎么处理图片的呢?
一、前端图片发展三阶段
- 第一阶段: 使用
fireworks
软件来切图,直接通过background-image
或img
来引入网页中。一个网页那么多的图片,于是为了减少Http
请求,把多张小图片放到一张png
格式的图片上,再通过background-position
来定位使用。
这是一个精灵图也称雪碧图:
![](https://img.haomeiwen.com/i16069544/881c2ced39b7c6fe.png)
- 第二阶段:字体图标阶段,前期比较出名的就是 fontawesome 现在常用的就是阿里妈妈 iconfont,例如面包屑导航的返回,首页小房子这种就变成字体图标了。
![](https://img.haomeiwen.com/i16069544/75889a44ef06d518.png)
- 先了解下编码 于CSS emoji字体和OpenType-SVG我所知道的一些事
- 再来看下实践 「每日一题」聊一聊字体图标的实现原理
最大的优点就是可以像使用字体一样使用图标,不用担心图标大小和字体对齐的问题,另外 CSS 的 font 属性对图标都可以生效,尤其是 font-size
和 color
贼好用。但是有个缺点只支持单色图标,一般默认是灰不拉几的颜色,一旦 color 改变,是红就全红,蓝就全蓝。
字体图标原理:不知道你有没有疑问🤔️?字体图标为什么可以用字体来表示,明明是图片呀。浏览器的文字之所以能够识别我们写的字是因为指定了编码,编码库里面有这个字,涉及到的就是 unicode 编码:
- 第三阶段:SVG 的 Sprites 技术。支持彩色图标,色彩更加丰富类似这种,使用没啥难点,想了解一点原理参考张鑫旭大大的 未来必热:SVG Sprites技术介绍:
彩色图标色彩更加丰富类似这种:
二、现在项目如何使用图片
现在我们公司图片项目都用阿里妈妈的 iconfont 来管理,一般在项目中:
-
类似面包屑导航这样的图标使用字体图标,antd 和 elementUi 等主流UI框架,都有 Icon 组件,封装使用即可。
-
稍微大一点的图片会使用
webpack
的url-loader
插件来把图片转化成 base64 格式,达到减少 Http 请求的目的。 -
紧接第二步骤,如果项目中大于 100kb 的图片过多,都转化为 base64 会造成 webpack 打包的 JS 文件过大,所以就的思考到底要不要用精灵图。
-
也可以使用 ES11 的
import().then()
API,动态加载图片。
三、精灵图的优化
自己一个前端写项目可能哪里用到直接上 background-position
等属性卡卡的怼起来了,多人写作可能会维护一个 CSS 文件,全部按图片名称命名好类名,用到直接使类名。
但是不容忽视缺点很大,设计师新增一个图标或删除一个图标就要动精灵图,一不小心就可能定位全部乱了。
如果有一个工具,能把指定文件夹中的一个个小图标打包成一个精灵图,并自动生成 CSS 类文件,一点不需要我们手动维护就好了。有请 webpack-spritesmith
他就能干这个事情。
先推荐一个教程 vue-cli3中使用webpack-spritesmith配置雪碧图 这个作者写的挺详细的。
用 Vue 的项目自己动手实践下,先用 CSS 来实现。
四、上手使用 webpack-spritesmith
GitHub 源码地址: 上手使用 webpack-spritesmith源码
创建的目录结构:
|--webpack.config.js
|--index.html
|--img
| |--sprite.css//插件生成
| |--sprite.png/插件生成
| |--sprites
| | |--img_1x.png
| | |--img_2x.png
| | |--img_3x.png
| | |--img_4x.png
|--package-lock.json
|--package.json
|--|--js
|--App.vue
|--main.js
各个文件的内容详细内容为:
package.json
的内容:
{
"name": "webpack-spritesmith-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "rm -rf ./dist && webpack"
},
"author": "",
"license": "ISC",
"dependencies": {
"copy-webpack-plugin": "^6.0.3",
"css-loader": "^3.6.0",
"file-loader": "^6.0.0",
"less-loader": "^6.2.0",
"style-loader": "^1.2.1",
"vue": "^2.6.11",
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
"webpack-spritesmith": "^1.1.0"
}
}
webpack.config.js
内容:
//引入node模块,一会要操作文件,输入,生成,输出都需要这玩意。
const path = require('path');
// 主角,必须引入。要不然咋用
const SpritesmithPlugin = require('webpack-spritesmith');
// vue-loader
const VueLoaderPlugin = require('vue-loader/lib/plugin');
// copy插件,把一个文件或目录拷贝到另一个地方,这里用来拷贝index.html
// https://www.npmjs.com/package/copy-webpack-plugin
const CopyWebpackPlugin = require('copy-webpack-plugin');
// 所有的配置都在这个导出里面
module.exports = {
mode: "development",
//配置入口文件
entry: "./js/main.js",
//配置产出文件
output: {
//产出文件文件夹
path: path.resolve(__dirname, "dist"),
//产出文件的文件名
filename: "bundle.js"
},
//实时监测文件更新,一旦文件更新了,就重新合并打包一份
watch: true,
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.css$/, use: [
'style-loader',
'css-loader'
]
},
{
test: /\.png$/, use: [
'file-loader?name=[hash].[ext]'
]
}
]
},
resolve: {
/*
细节坑。文档里面写着 需要resolve,引入图片生成的位置,
不加这行会报错。因为github,Readme里面有这句话
resolve contains location of where generated image is
(要把生成的地址resolve到modules里面。不写就错)
一定要加,血的教训啊
*/
modules: ["node_modules", "./img"],
extensions: [".js", ".json", ".jsx", ".css",".vue"]
},
stats: 'errors-only',
// 定义一个插件数组。用来覆盖,在里面使用我们的主角
plugins: [
new SpritesmithPlugin({
/*
目标小图标,这里就是你需要整合的小图片的老巢。
现在是一个个的散兵,把他们位置找到,合成一个
*/
src: {
cwd: path.resolve(__dirname, './img/sprites'),
glob: '*.png'
},
// 输出雪碧图文件及样式文件,这个是打包后,自动生成的雪碧图和样式,自己配置想生成去哪里就去哪里
target: {
image: path.resolve(__dirname, './img/sprite.png'),
css: [
// webpack,会自动生成一个sprite.css的样式
[path.resolve(__dirname, './img/sprite.css')],
]
},
// 生成的样式文件中调用雪碧图地址写法(Readme这么写的)
// 不写就是全局的绝对图片路径
apiOptions: {
cssImageRef: '~sprite.png'
},
// 让合成的每个图片有一定的距离,否则就会紧挨着,不好使用
spritesmithOptions: {
padding: 20
}
}),
new VueLoaderPlugin(),
new CopyWebpackPlugin({
patterns: [
{ from: __dirname + "/index.html", to: __dirname + "/dist" }
],
})
]
}
index.html
内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack-spritesmith</title>
</head>
<body>
<div id="app"></div>
<script src="./bundle.js"></script>
</body>
</html>
main.js
import Vue from "vue";
import App from "./App.vue";
new Vue({
el:"#app",
render(h){
return h(App);
}
});
App.vue
类的名字默认都是 icon-图片名例如:``icon-img_1x
:
<template>
<div>
<div class="icon-img_1x"></div>
</div>
</template>
<script>
export default {
}
</script>
<style lang="css">
@import "../img/sprite.css";
</style>
最后运行代码:
npm run dev
sprite.css
文件里面四个类选择器,大致长成这样,简单的图片引入和定位:
.icon-img_1x {
background-image: url(~sprite.png);
background-position: 0px 0px;
width: 120px;
height: 120px;
}
成功访问效果:
![](https://img.haomeiwen.com/i16069544/62e5fa4938a7ebc0.png)
好处大大的,终于不用我们合并图片和调整 position 的位置。
一般项目中都不是直接使用 CSS 的,往往会使用类似 less 这种预处理器,我就以 less 来演示下。webpack-spritesmith
编译的 CSS 就是简单类的使用,但是如果使用使用 sass
或 less
编译就会用到一些稀奇古怪的语法,为了能看懂 less
编译之后的文件,我们需要了解一下 less
的知识,推荐 Less教程:
不想搭建环境,你也可以使用这个网站在线实时调试:https://lesstester.com/
五、less 前置知识了解
- less 的条件判断和循环
条件判断使用 when 来实现的,循环使用 mixin 和 when 来递归调用的。具体点击:less 使用笔记
- Less 的列表函数
列表函数函数一共两个方法:
1. Length 列表的长度。
// 列表可以使用空格或逗号间隔
p{
@list: "a", "b", "c", "d", "e", "f", "g", "h", "k", "m", "l", "s";
@font-size: length(@list);
font-size: @font-size * 1px;
}
p{
@list: "a" "b" "c" "d" "e" "f" "g" "h" "k" "m" "l" "s";
@font-size: length(@list);
font-size: @font-size * 1px;
}
//上面两种写法等效,都会编译为:
p {
font-size: 12px;
}
//less 很麻烦单位连接 * 1px
- Extract 返回列表中指定位置的值。
extract 就像访问数组一样,提取下标对应位置的值,不过 extract 的下标是从1开始的
p{
@list: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17;
@font-size: extract(@list,16);
font-size: @font-size * 1px;
}
编译之后:
p {
font-size: 16px;
}
上面是一维的列表,我们来看看二维列表:
p{
@a: 1 2 3 4 5 6 7 8;
@b: 9 10 11 12 13 14 15 16;
@c : @a @b;
font-size: extract(extract(@c,2),8) * 1px;
}
编译之后:
p {
font-size: 16px;
}
p{
@a: 1 2 3 4 5 6 7 8;
@b: 9 10 11 12 13 14 15 16;
@c : 17 18 19 20;
@d: @a @b;
@e: @c @d;
font-size: extract(extract(@c,2),8) * 1px;
}
还可以三维列表:
p{
@a: 1 2 3 4 5 6 7 8;
@b: 9 10 11 12 13 14 15 16;
@c : 17 18 19 20;
@d: @a @b;
@e: @c @d;
font-size: extract(extract(extract(@e,2),2),1) * 1px;
}
编译之后:
p {
font-size: 9px;
}
知道多维列表和单维列表,就能明白为什么经过`webpack-spritesmith`编译之后生成的`less`文件对只有一个精灵图是无效的。
改改循环,相应的我们可以写出来处理一张图片的 less 代码,但是没必要因为一张图完全可以直接引入
.sprites(@sprites, @i: 10) when (@i = length(@sprites)) {
@sprite-name: e(extract(@sprites, length(@sprites)));
.@{sprite-name} {
.sprite(@sprites);
}
}
- Less 字符串函数
〜"some_text"中的任何内容将显示为 some_text 。
font-size: ~"12px";
编译之后:
font-size: 12px;
font-size: e("12px");
编译之后:
font-size: 12px;
〜"xxx" === e("xxx")
%此函数格式化一个字符串。 它可以写成以下格式:
font-size: e(%("12%d",px));
编译之后:
font-size: 12px;
六、看下 webpack-spritesmith 处理过的 less 文件
使用 less 精灵图要求必须两张以上,否则无法使用,为什么一张图片不行,在上面二维列表那里。
/*
LESS 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 name;
At the bottom of this section, we provide information about the spritesheet itself
@spritesheet: width height image @spritesheet-sprites;
*/
@img-1x-name: 'img_1x';
@img-1x-x: 0px;
@img-1x-y: 0px;
@img-1x-offset-x: 0px;
@img-1x-offset-y: 0px;
@img-1x-width: 120px;
@img-1x-height: 120px;
@img-1x-total-width: 260px;
@img-1x-total-height: 120px;
@img-1x-image: '~sprite.png';
@img-1x: 0px 0px 0px 0px 120px 120px 260px 120px '~sprite.png' 'img_1x';
@img-2x-name: 'img_2x';
@img-2x-x: 140px;
@img-2x-y: 0px;
@img-2x-offset-x: -140px;
@img-2x-offset-y: 0px;
@img-2x-width: 120px;
@img-2x-height: 120px;
@img-2x-total-width: 260px;
@img-2x-total-height: 120px;
@img-2x-image: '~sprite.png';
@img-2x: 140px 0px -140px 0px 120px 120px 260px 120px '~sprite.png' 'img_2x';
@spritesheet-width: 260px;
@spritesheet-height: 120px;
@spritesheet-image: '~sprite.png';
@spritesheet-sprites: @img-1x @img-2x;
@spritesheet: 260px 120px '~sprite.png' @spritesheet-sprites;
/*
The provided classes are intended to be used with the array-like variables
.icon-home {
.sprite-width(@icon-home);
}
.icon-email {
.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>
*/
.sprite-width(@sprite) {
width: extract(@sprite, 5);
}
.sprite-height(@sprite) {
height: extract(@sprite, 6);
}
.sprite-position(@sprite) {
@sprite-offset-x: extract(@sprite, 3);
@sprite-offset-y: extract(@sprite, 4);
background-position: @sprite-offset-x @sprite-offset-y;
}
.sprite-image(@sprite) {
@sprite-image: extract(@sprite, 9);
background-image: e(%('url(%a)', e(@sprite-image)));
}
.sprite(@sprite) {
.sprite-image(@sprite);
.sprite-position(@sprite);
.sprite-width(@sprite);
.sprite-height(@sprite);
}
/*
The `.sprites` mixin generates identical output to the CSS template
but can be overridden inside of LESS
This must be run when you have at least 2 sprites.
If run with a single sprite, then there will be reference errors.
.sprites(@spritesheet-sprites);
*/
.sprites(@sprites, @i: 1) when (@i <= length(@sprites)) {
@sprite: extract(@sprites, @i);
@sprite-name: e(extract(@sprite, 10));
.@{sprite-name} {
.sprite(@sprite);
}
.sprites(@sprites, @i + 1);
}
了解完 less 的基础知识就会发现没啥难得,就是通过mixin接受列表参数,来无限循环展开类。Vue 中使用:
<template>
<div>
<div class="img_1x"></div>
</div>
</template>
<script>
export default {
}
</script>
<style lang="less">
@import "../img/sprite.less";
.sprites(@spritesheet-sprites);
//调用.sprites(@spritesheet-sprites);less会生成如下类
// */
// .img_1x {
// background-image: url(~sprite.png);
// background-position: 0px 0px;
// width: 120px;
// height: 120px;
// }
// .img_2x {
// background-image: url(~sprite.png);
// background-position: -140px 0px;
// width: 120px;
// height: 120px;
// }
</style>
看到编译的结果是不是很失望,使用 less 和直接用 CSS 编译的结果基本没啥区别,这弄的花里胡哨的还把我们给整蒙了。最大的好处就是可以在项目使用。
七、使用 webpack-spritesmith 的自定义模版
我们能看到经过 webpack-spritesmith
处理的精灵图自动添加的 CSS 属性都是比较简单的。引入个图片定个位就没了。但是如果项目中,出现这种下面图片👇:
![](https://img.haomeiwen.com/i16069544/0ca7d353412b3fff.png)
这种图片特别的小,如果 UI 一比一切给前端,图片展现就比较模糊,所以 UI 会切成二倍图交给我们,然后我们前端使用 CSS3 的
transform: scale(.5);
,给缩小下就行了,完美解决问题。如果这种图片过多能不能让 webpack-spritesmith
来帮我们处理下呢?答案是可以利用 webpack-spritesmith
的自定义模版。
完整 webpack.config.js
加注释如下:
//引入node模块,一会要操作文件,输入,生成,输出都需要这玩意。
const path = require('path');
const{ writeFile } = require("fs");
// 主角,必须引入。要不然咋用
const SpritesmithPlugin = require('webpack-spritesmith');
// vue-loader
const VueLoaderPlugin = require('vue-loader/lib/plugin');
// copy插件,把一个文件或目录拷贝到另一个地方,这里用来拷贝index.html
// https://www.npmjs.com/package/copy-webpack-plugin
const CopyWebpackPlugin = require('copy-webpack-plugin');
/*
这里我们可以自己修改生成的模板样式,默认 webpack,会根据你的target里的配置,自动生成一个sprite.(c|le|sc)ss的样式文件
如果自动生成的不满意,
我们可以在这里修改,
可以自己打印一下 data里面的参数,看着就会大概明白(先看下面的配置,最后看这个模板)
*/
const customSpritesFunction = function (data) {
// 观察data 到底是个啥
// console.log(data);
writeFile("./data.json",JSON.stringify(data),function(err){
if(!err){
console.log("写入成功!");
}
});
// 观察data发现是个对象其中属性items和属性sprites都是数组,而且内容相同,截取其数组第一个数据
// 可以看到items和属性sprites里面存储的是图片的信息。宽高大小路径等各种参数
/*{
"x": 0,
"y": 0,
"width": 120,
"height": 120,
"name": "img_1x",
"source_image": "/Users/lixinwei/Desktop/webpack-spritesmith-test/img/sprites/img_1x.png",
"image": "~sprite.png",
"escaped_image": "~sprite.png",
"total_width": 260,
"total_height": 260,
"offset_x": 0,
"offset_y": 0,
"px": {
"x": "0px",
"y": "0px",
"offset_x": "0px",
"offset_y": "0px",
"height": "120px",
"width": "120px",
"total_height": "260px",
"total_width": "260px"
}
},
*/
// data.sprites数组图片信息,根据图片信息自己把一些属性组合到对应图片的类名里面,
return data.sprites.map(function (sprite) {
return '.icon-NAME-box{\n\tdisplay: inline-block;\n\toverflow: hidden;\n\twidth: BOXWIDTHpx;\n\theight: BOXHEIGHTpx;\n}\n.icon-NAME{\n\tdisplay: inline-block;\n\twidth: WIDTHpx;\n\theight: HEIGHTpx;\n\tbackground-image: url(IMG);\n\tbackground-position: Xpx Ypx;\n\ttransform-origin: 0 0;\n\ttransform: scale(NUM);\n}'
.replace(/IMG/g,sprite.image)
.replace(/NAME/g, sprite.name)
.replace(/WIDTH/g, sprite.width)
.replace(/HEIGHT/g, sprite.height)
.replace(/X/g, sprite.offset_x)
.replace(/Y/g, sprite.offset_y)
.replace(/BOXHEIGHT/g, sprite.name.indexOf('_2x') > -1 ? sprite.height / 2 : sprite.height)
.replace(/BOXWIDTH/g, sprite.name.indexOf('_2x') > -1 ? sprite.width / 2 : sprite.width)
.replace(/NUM/g, sprite.name.indexOf('_2x') > -1 ? 0.5 : 1);
}).join('\n');
// 最后三行replace就是根据图片名是否决定进行缩放
};
// 所有的配置都在这个导出里面
module.exports = {
mode: "development",
//配置入口文件
entry: "./js/main.js",
//配置产出文件
output: {
//产出文件文件夹
path: path.resolve(__dirname, "dist"),
//产出文件的文件名
filename: "bundle.js"
},
//实时监测文件更新,一旦文件更新了,就重新合并打包一份
watch: true,
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.(c|le)ss$/, use: [
'style-loader',
'css-loader',
'less-loader'
]
},
{
test: /\.png$/, use: [
'file-loader?name=[hash].[ext]'
]
}
]
},
resolve: {
/*
细节坑。文档里面写着 需要resolve,引入图片生成的位置,
不加这行会报错。因为github,Readme里面有这句话
resolve contains location of where generated image is
(要把生成的地址resolve到modules里面。不写就错)
一定要加,血的教训啊
*/
modules: ["node_modules", "./img"],
extensions: [".js", ".json", ".jsx", ".css",".vue"]
},
stats: 'errors-only',
// 定义一个插件数组。用来覆盖,在里面使用我们的主角
plugins: [
new SpritesmithPlugin({
/*
目标小图标,这里就是你需要整合的小图片的老巢。
现在是一个个的散兵,把他们位置找到,合成一个
*/
src: {
cwd: path.resolve(__dirname, './img/sprites'),
glob: '*.png'
},
// 输出雪碧图文件及样式文件,这个是打包后,自动生成的雪碧图和样式,自己配置想生成去哪里就去哪里
target: {
image: path.resolve(__dirname, './img/sprite.png'),
css: [
// webpack,会自动生成一个sprite.css的样式
[path.resolve(__dirname, './img/sprite.less'),{
// 引用自己的模板
format: 'custom_sprites_template'
}]
]
},
// 自定义模板入口,我们需要基本的修改webapck生成的样式,上面的大函数就是我们修改的模板
customTemplates: {
'custom_sprites_template': customSpritesFunction
},
// 生成的图片在 API 中被引用的路径。
//简单来说,就是你上面输出了 image 和 css ,那么在 css 用什么样的路径书写方式来引用 image 图片(可以是别名,或相对路径)
// 不写就是全局的绝对图片路径
apiOptions: {
cssImageRef: '~sprite.png'
},
// 让合成的每个图片有一定的距离,否则就会紧挨着,不好使用
spritesmithOptions: {
padding: 20
}
}),
new VueLoaderPlugin(),
new CopyWebpackPlugin({
patterns: [
{ from: __dirname + "/index.html", to: __dirname + "/dist" }
],
})
]
}
App.vue 使用:
//引入node模块,一会要操作文件,输入,生成,输出都需要这玩意。
const path = require('path');
const{ writeFile } = require("fs");
// 主角,必须引入。要不然咋用
const SpritesmithPlugin = require('webpack-spritesmith');
// vue-loader
const VueLoaderPlugin = require('vue-loader/lib/plugin');
// copy插件,把一个文件或目录拷贝到另一个地方,这里用来拷贝index.html
// https://www.npmjs.com/package/copy-webpack-plugin
const CopyWebpackPlugin = require('copy-webpack-plugin');
/*
这里我们可以自己修改生成的模板样式,默认 webpack,会根据你的target里的配置,自动生成一个sprite.(c|le|sc)ss的样式文件
如果自动生成的不满意,
我们可以在这里修改,
可以自己打印一下 data里面的参数,看着就会大概明白(先看下面的配置,最后看这个模板)
这个文档也稍微有点用,放上来
https://github.com/qq20004604/webpack-study/tree/master/8%E3%80%81%E6%8F%92%E4%BB%B6/webpack-spritesmith
*/
const customSpritesFunction = function (data) {
// 观察data 到底是个啥
// console.log(data);
writeFile("./data.json",JSON.stringify(data),function(err){
if(!err){
console.log("写入成功!");
}
});
// 观察data发现是个对象其中属性items和属性sprites都是数组,而且内容相同,截取其数组第一个数据
// 可以看到items和属性sprites里面存储的是图片的信息。宽高大小路径等各种参数
/*{
"x": 0,
"y": 0,
"width": 120,
"height": 120,
"name": "img_1x",
"source_image": "/Users/lixinwei/Desktop/webpack-spritesmith-test/img/sprites/img_1x.png",
"image": "~sprite.png",
"escaped_image": "~sprite.png",
"total_width": 260,
"total_height": 260,
"offset_x": 0,
"offset_y": 0,
"px": {
"x": "0px",
"y": "0px",
"offset_x": "0px",
"offset_y": "0px",
"height": "120px",
"width": "120px",
"total_height": "260px",
"total_width": "260px"
}
},
*/
// data.sprites数组图片信息,根据图片信息自己把一些属性组合到对应图片的类名里面,
return data.sprites.map(function (sprite) {
return '.icon-NAME-box{\n\tdisplay: inline-block;\n\toverflow: hidden;\n\twidth: BOXWIDTHpx;\n\theight: BOXHEIGHTpx;\n}\n.icon-NAME{\n\tdisplay: inline-block;\n\twidth: WIDTHpx;\n\theight: HEIGHTpx;\n\tbackground-image: url(IMG);\n\tbackground-position: Xpx Ypx;\n\ttransform-origin: 0 0;\n\ttransform: scale(NUM);\n}'
.replace(/IMG/g,sprite.image)
.replace(/NAME/g, sprite.name)
.replace(/WIDTH/g, sprite.width)
.replace(/HEIGHT/g, sprite.height)
.replace(/X/g, sprite.offset_x)
.replace(/Y/g, sprite.offset_y)
.replace(/BOXHEIGHT/g, sprite.name.indexOf('_2x') > -1 ? sprite.height / 2 : sprite.height)
.replace(/BOXWIDTH/g, sprite.name.indexOf('_2x') > -1 ? sprite.width / 2 : sprite.width)
.replace(/NUM/g, sprite.name.indexOf('_2x') > -1 ? 0.5 : 1);
}).join('\n');
// 最后三行replace就是根据图片名是否决定进行缩放
};
// 所有的配置都在这个导出里面
module.exports = {
mode: "development",
//配置入口文件
entry: "./js/main.js",
//配置产出文件
output: {
//产出文件文件夹
path: path.resolve(__dirname, "dist"),
//产出文件的文件名
filename: "bundle.js"
},
//实时监测文件更新,一旦文件更新了,就重新合并打包一份
watch: true,
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.(c|le)ss$/, use: [
'style-loader',
'css-loader',
'less-loader'
]
},
{
test: /\.png$/, use: [
'file-loader?name=[hash].[ext]'
]
}
]
},
resolve: {
/*
细节坑。文档里面写着 需要resolve,引入图片生成的位置,
不加这行会报错。因为github,Readme里面有这句话
resolve contains location of where generated image is
(要把生成的地址resolve到modules里面。不写就错)
一定要加,血的教训啊
*/
modules: ["node_modules", "./img"],
extensions: [".js", ".json", ".jsx", ".css",".vue"]
},
stats: 'errors-only',
// 定义一个插件数组。用来覆盖,在里面使用我们的主角
plugins: [
new SpritesmithPlugin({
/*
目标小图标,这里就是你需要整合的小图片的老巢。
现在是一个个的散兵,把他们位置找到,合成一个
*/
src: {
cwd: path.resolve(__dirname, './img/sprites'),
glob: '*.png'
},
// 输出雪碧图文件及样式文件,这个是打包后,自动生成的雪碧图和样式,自己配置想生成去哪里就去哪里
target: {
image: path.resolve(__dirname, './img/sprite.png'),
css: [
// webpack,会自动生成一个sprite.css的样式
[path.resolve(__dirname, './img/sprite.less'),{
// 引用自己的模板
format: 'custom_sprites_template'
}]
]
},
// 自定义模板入口,我们需要基本的修改webapck生成的样式,上面的大函数就是我们修改的模板
customTemplates: {
'custom_sprites_template': customSpritesFunction
},
// 生成的图片在 API 中被引用的路径。
//简单来说,就是你上面输出了 image 和 css ,那么在 css 用什么样的路径书写方式来引用 image 图片(可以是别名,或相对路径)
// 不写就是全局的绝对图片路径
apiOptions: {
cssImageRef: '~sprite.png'
},
// 让合成的每个图片有一定的距离,否则就会紧挨着,不好使用
spritesmithOptions: {
padding: 20
}
}),
new VueLoaderPlugin(),
new CopyWebpackPlugin({
patterns: [
{ from: __dirname + "/index.html", to: __dirname + "/dist" }
],
})
]
}
成功截图:
![](https://img.haomeiwen.com/i16069544/ec2b1ced9d1bb757.png)
八、iconfont 的使用
结合 React 或 Vue 框架对 iconfont 的图标进行封装,就能像使用 Antd 的 Icon 组件一样使用 iconfont 的图标了,入门使用的话推荐 iconfont 使用教程,没有比这个官网教程更专业的了。
不过 React 在使用 symbol 方法用 iconfont 图标有个坑,不能使用,然后我去 iconFont 的 GitHub 仓库提问官方也不理会,只能自问自答了,React 框架中使用 symbol引用
出错
九、最后
现在我是更能体会上课听老师讲课,相比现在简直都要幸福死了,老师上课已有知识无脑传授,我们无脑接受,不用实践也不管自己会不会,能过考试就完了,考前都不鸟它考完就更不鸟了😂。Alas 为了弄明白这个知识点花了两天一夜,真是太费时间了。🤪
最近要换房子了,又是挺麻烦的事,我要是能搬到五道口北京林业大学旁边就好了😇。
当前时间 Tuesday, July 14, 2020 01:27:50