- 初次发布于2020-02-19
- 第二次编辑于2021-03-13,更新svg-symbol
前言
用了矢量图标,
UI再也不吐槽你页面的图标放大后有点糊了…
PM再也不会嫌弃你画的页面图片太大,太占load time了…
你也不用因为一个hover状态,就写一段换图片src的逻辑了…
矢量图标
- 不会因为放大/缩小而导致图标变模糊,即可以任意定义尺寸的icon。
- 在现代前端项目中,除了任意定义尺寸,对图标的要求还有定义颜色等等。
字体图标
- 以字体的形式定义的图标,可以像字体一样定义尺寸,颜色。
- 优点:体积小(用到的图标越多性价比越高);样式便于控制,与字体一样可以调节font-size,color,阴影,旋转,透明度,且兼容低版本浏览器。
- 方法与原理:UI提供svg矢量图 -> 用工具将svg转换成字体文件 -> 前端在css中自定义font-face,通过unicode的方式使用图标。
- 可以使用主流的字体图标库:fontAwesome,阿里的iconfont。
- 也可以使用自动化工具,例如@ztwx/webfont:可以自动化上述过程,运行工具打开网页后,上传svg文件,自动更新代码中的字体文件和css文件。
- 缺点:需要UI合作;每次图标变更都需要重新生成字体文件,更改css中定义的font-face和class。
- 前端引用iconfont主流方式:
直接使用Unicode字符
- 兼容性最好,支持 IE6+,及所有现代浏览器。
- 支持按字体的方式去动态调整图标大小,颜色等等。
- 但是因为是字体,所以不支持多色。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/*第一步:自定义@font-face,引用字体文件*/
@font-face {
font-family: 'iconfont';
src: url('iconfont.eot');
src: url('iconfont.eot?#iefix') format('embedded-opentype'),
url('iconfont.woff2') format('woff2'),
url('iconfont.woff') format('woff'),
url('iconfont.ttf') format('truetype'),
url('iconfont.svg#iconfont') format('svg');
}
/*第二步:定义使用 iconfont 的样式*/
.iconfont {
font-family: "iconfont" ;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/*第三步:挑选相应图标并获取unicode编码,应用于页面*/
<span class="iconfont">3</span>
font-class(使用最多)
- unicode变体,解决了unicode编码语义不明确的问题,书写更直观。可以很容易分辨这个 icon 是什么,著名的FontAwesome即使用该方式
- 兼容性良好,支持 IE8+,及所有现代浏览器。
- 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
本质上还是字体,所以多色图标还是不支持的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22/*第一步:自定义@font-face,引用字体文件*/
@font-face {
font-family: 'iconfont';
src: url('iconfont.eot');
src: url('iconfont.eot?#iefix') format('embedded-opentype'),
url('iconfont.woff2') format('woff2'),
url('iconfont.woff') format('woff'),
url('iconfont.ttf') format('truetype'),
url('iconfont.svg#iconfont') format('svg');
}
/*第二步:定义iconfont样式*/
.iconfont {
font-family: "iconfont" ;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/*第三步:定义每个icon unicode 样式类*/
.icon-gouwuche:before {
content: "\e669";
}1
2
3
4<!-- 第四步:引入1,2步的css -->
<link rel="stylesheet" href="./iconfont.css">
<!-- 第五步:挑选相应图标并使用定义的类名,应用于页面: -->
<span class="iconfont icon-xxx"></span>
Symbol
- 本质是svg,支持多色。
- 较新的使用方式,兼容性较差,支持 IE9+,及现代浏览器。
- 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
浏览器渲染 SVG 的性能一般,还不如 png。
1
2<!-- 第一步:引入js代码 -->
<script src="./iconfont.js"></script>1
2
3
4
5
6
7
8/* 第二步:加入通用css样式(引入一次即可) */
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}1
2
3
4<!-- 第三步:挑选相应图标并获取类名,应用于页面: -->
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-xxx"></use>
</svg>
icon最佳实践:svg图标
- 除了字体图标,现代前端项目中使用更多的是Svg图标
<svg>
及其一系列元素,用于矢量图像的结构、绘制与布局<symbol>元素
,是可复用的svg图像,用来定义一个图形模板对象,它可以用一个<use>
元素实例化。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<svg>
<!-- symbol定义模板(symbol本身不会显示图像 -->
<symbol id="sym01" viewBox="0 0 150 110">
<circle cx="50" cy="50" r="40" stroke-width="8" stroke="red" fill="red"/>
<circle cx="90" cy="60" r="40" stroke-width="8" stroke="green" fill="white"/>
</symbol>
<!-- 使用use元素才能实例化 -->
<use xlink:href="#sym01"
x="0" y="0" width="100" height="50"/>
<use xlink:href="#sym01"
x="0" y="50" width="75" height="38"/>
<use xlink:href="#sym01"
x="0" y="100" width="50" height="25"/>
</svg>symbolId
:在<use>
元素的xlink:href
属性需要指向symbol元素的id- 优点:
- svg图标拥有字体图标的各种优点此外
- svg不需要UI转成字体、定义font-face,只需要原始的svg代码,即可使用。
- 通过现代前端工具整合,svg图标变更时,只需要变更单个svg文件,耦合度低
svg symbol应用于现代前端框架
- 创建svg目录,存放一个svg文件,例如
@/assets/svg/test.svg
在webpack中配置
svg-sprite-loader
,匹配上述的svg文件,并将其symbolId配置为svg文件名,详情参考svg-sprite-loader
文档1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 以下为vue cli4的配置
module.exports = {
chainWebpack(config){
// 1. 将./src/assets/svg排除在vue的默认webpack配置的svg规则之外
config.module.rule('svg')
.exclude.add(resolve('./src/assets/svg'))
// 2. 添加一条icons规则
config.module.rule('icons')
.test(/\.svg$/) // 匹配svg后缀的文件
.include.add(resolve('./src/assets/svg')).end() // 添加规则生效的路径
.use('svg-sprite-loader') // 使用svg-sprite-loader插件
.loader('svg-sprite-loader') // 加载svg-sprite-loader插件
.options({symbolId: 'icon-[name]'}) // name:文件名,此处的symbolId即svg的xlink:href属性,xlink:href="#icon-文件名"
}
...
}在vue模板中即可直接使用
1
2
3
4
5
6
7
8
9
10
11// text.vue
<template>
...
<svg :class="svgClass" v-on="$listeners">
<use xlink:href="#icon-test"></use>
</svg>
...
</template>
import '@/assets/svg/test.svg';
...更进一步的封装:不需要每个icon都import;
<svg-icon>
组件;
通过webpack上下文,将svg文件作为固定上下文,即可避免手动import,用到了webpack中的require.context api
1
2
3
4
5
6// @/assets/svg/index.js
// 使用 webpack提供的require.context()指定svg为固定上下文
function importAll (r) {
r.keys().forEach(r)
}
importAll(require.context('@/assets/svg', false, /\.svg$/))在main.js中引入上述js文件
1
import '@/assets/svg/index.js' // svg上下文
封装icon组件
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<template>
<svg :class="svgClass" v-on="$listeners">
<use :xlink:href="iconName" />
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
// iconClass要和svg的文件名一致
iconClass: {
type: String,
required: true
},
// className可以用来定制样式
className: {
type: String,
default: ''
}
},
computed: {
iconName () {
return `#icon-${this.iconClass}`
},
svgClass () {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>在main.js中注册全局组件
1
2import SvgIcon from '@/components/common/SvgIcon.vue'
Vue.component('SvgIcon', SvgIcon)使用
1
<svg-icon iconClass="test"></svg-icon>