svg半小时全方位入门
1 - svg 应用场景和优势
canvas 快速入门
canvas 是 HTML5 的新特性,它允许我们使用 canvas 元素在网页上通过 JavaScript 绘制图像。
- 入门案例:绘制矩形、线段、圆、点
<!DOCTYPE html>
<html>
<head></head>
<body>
<!-- 声明canvas画布宽高为800 -->
<canvas id="canvas" width="800" height="800"></canvas>
</body>
<script>
// 根据id获取dom元素
const canvas = document.getElementById('canvas')
// 通过dom元素获取到canvas对象
const ctx = canvas.getContext('2d')
// 修改填充色为红色
ctx.fillStyle = 'red'
// 在画布(0, 0)位置为起点绘制长宽为50的矩形
ctx.fillRect(0, 0, 50, 50)
// 开启一条路径
ctx.beginPath()
// 线宽为1px
ctx.lineWidth = 1
// 边框颜色为蓝色
ctx.strokeStyle = 'blue'
// 起点为(100, 100)
ctx.moveTo(100, 100)
// 画到(250, 75)的位置
ctx.lineTo(250, 75)
// 接着画到(300, 100)的位置
ctx.lineTo(300, 100)
// 绘制线段
ctx.stroke()
// 开启一条路径
ctx.beginPath()
// 线宽为2px
ctx.lineWidth = 2
// 边框颜色为绿色
ctx.strokeStyle = 'green'
// 填充色为红色
ctx.fillStyle = 'red'
// 设置圆心为(200, 200) 半径50 起始角度为0 结束角度为2pi 即360°
ctx.arc(200, 200, 50, 0, 2 * Math.PI)
// 绘制线段
ctx.stroke()
// 填充
ctx.fill()
// 绘制一个点 本质上是1px的线段
ctx.beginPath()
ctx.lineWidth = 1
ctx.strokeStyle = 'blue'
ctx.moveTo(300, 300)
ctx.lineTo(301, 301)
ctx.stroke()
</script>
</html>
canvas绘图流程
- 编写 canvas 标签(注意指定宽高)
- 获取 cancas DOM 对象
- 获取 canvas 对象
- 设置绘图属性
- 调用绘图 API
SVG 快速入门
SVG 是一种基于 XML 的图像文件格式,它的英文全称为 Scalable Vector Graphics,意思为可缩放的矢量图形。
- 入门案例:绘制矩形、线段、圆、点
<!DOCTYPE html>
<html>
<head></head>
<body>
<svg width="800" height="800">
<!-- 绘制一个50x50的矩形, 填充红色, 边框1px, 黑色 -->
<rect width="50" height="50" style="fill: red;stroke-width: 1px;stroke: rgb(0,0,0)" />
<!-- 绘制一条线段, 坐标从(100,100)到(250,75), 边框1px, 蓝色 -->
<line x1="100" y1="100" x2="250" y2="75" style="stroke-width: 1px;stroke: blue" />
<!-- 绘制另一条线段, 坐标从(250,75)到(300,100), 边框1px, 蓝色 -->
<line x1="250" y1="75" x2="300" y2="100" style="stroke-width: 1px;stroke: blue" />
<!-- 绘制一个圆, 圆心位于(200,200), 半径50px, 边框绿色2px, 填充红色 -->
<!-- stroke,stroke-width,fill等样式属性也可以放到style中 -->
<circle cx="200" cy="200" r="50" stroke="green" stroke-width="2" fill="red" />
<!-- 绘制一个点, 本质其实是线段 -->
<line x1="300" y1="300" x2="301" y2="301" style="stroke-width: 1px;stroke: blue" />
</svg>
</body>
</html>
对比
- canvas 和 SVG 单从绘图的角度来看,从实现效果上看是比较类似的,但两者实际的编码过程完全不同。
- SVG 通过 dom 进行编程,canvas 通过 JavaScript API 的方式进行编程
- SVG 可以选中具体的dom,因为他们本身就是 html 标签;而 canvas 的交互全部都需要通过 JavaScript API 进行
- 所以 canvas 有更高的绘图效率,尤其在绘制动画的场景下可以更胜一筹,但是 svg 的矢量图特性,可以让我们在放大的场景下,仍然可以获得较清晰的图像,所以特别适合作为图标和矢量图的场景。
SVG 应用场景
- 绘制icon
- 绘制动画
SVG 优势
-
SVG 占用更小的容量
由于使用 XML 格式声明,因此通过 SVG 制作的图标或图像,其容量会比较小。对于一个纯色的200 x 200的简单图标,使用 png 格式图片需要大约8422字节,而使用 SVG 仅需1828字节。更小的容量意味着更快的加载速度,会有更好的用户体验。
-
SVG 具有更高的灵活性
对于 png 或其它图片格式而言,无法直接修改其颜色;而 SVG 通过在绘制路径上指定 fill 属性即可方便地更改颜色
2 - svg viewport和viewBox用法讲解
- viewport 是 svg图像的可见区域,即画布大小
- viewBox 是用于在画布上绘制 svg 图形的坐标系统
示例:
<!DOCTYPE html>
<html>
<head>
<style>
.parent {
color: green;
}
.parent div {
color: black;
margin-top: 10px;
}
</style>
</head>
<body>
<!-- svg 中的 currentColor 会继承父容器的 color -->
<div class="parent">
<div>viewport + viewBox 显示效果</div>
<!-- svg 会将 viewBox 指定的显示区域缩放为viewport大小 -->
<svg width="500" height="200" viewBox="0 0 50 20" style="border: 1px solid red">
<rect x="20" y="10" width="10" height="5" stroke="currentColor" fill="none"></rect>
</svg>
<div>不指定 viewBox 显示效果</div>
<svg width="500" height="200" style="border: 1px solid red">
<!-- 不指定 viewBox 则显示区域为画布大小 -->
<rect x="20" y="10" width="10" height="5" stroke="currentColor" fill="none"></rect>
</svg>
<div>在不指定 viewBox 的情况下手动使矩形显示效果与指定 viewBox 时显示一致</div>
<svg width="500" height="200" style="border: 1px solid red">
<!-- 手动将内容放大10倍 -->
<rect x="200" y="100" width="100" height="50" stroke-width="10" stroke="currentColor" fill="none"></rect>
</svg>
</div>
</body>
</html>
3 - 剖析 svg 中 preserveAspectRatio 属性
preserveAspectRatio 用于当 viewport 和 viewBox 宽高比不相同时,指定 viewBox 坐标系在 viewport 中是否完全可见,同时也可以指定它在 viewport 坐标系统中的位置
用法:
<svg preserveAspectRatio="<align> [<meetOrSlice>]"></svg>
-
<align>
属性值
使用以下对齐方式会保持宽高比:- 水平对齐方式
- xMin 左侧
- xMid 居中(默认值)
- xMax 右侧
- 垂直对齐方式
- YMin 顶部
- YMid 居中(默认值)
- YMax 底部
不保持宽高比,直接拉伸:
- none
- 水平对齐方式
-
<meetOrSlice>
可选属性值
如果提供的话,与<align>
间隔一个或多个空格,参数为以下二者之一:- meet (默认值)
保留宽高比,使整个 viewBox 能在 viewport 中可见,以 x 和 y 中较小的缩放比进行整体的缩放,类似css中的background-size: contain
- slice
保留宽高比,使用 viewport 覆盖 viewBox,以 x 和 y 中较大的缩放比进行整体的缩放,类似css中的background-size: cover
- meet (默认值)
由于
<align>
属性值比较简单,就是 viewBox 在 viewport 的上中下左中右的位置,此处不再赘述。
属性值说明
meet示例
<svg width="500" height="200" viewBox="0 0 200 200" preserveAspectRatio="xMidYMax meet">
<rect x="100" y="100" width="100" height="50" stroke-width="10" stroke="green" fill="none"></rect>
</svg>
上述配置的原理如下图:
meet 会取 x 和 y 中缩放比例较小的值,本例中
500/200=2.5
200/200=1
因此缩放比例为1,故对 viewBox 的大小进行缩放,由为缩放比例为1,所以没有变化
由为缩放比例为1,因此图形大小没有变化
图形位置由于是相对 viewBox 的坐标系确定的,因此在图中所示位置
slice 示例
<svg width="500" height="200" viewBox="0 0 200 200" preserveAspectRatio="xMidYMax slice">
<rect x="100" y="100" width="100" height="50" stroke-width="10" stroke="green" fill="none"></rect>
</svg>
上述配置的原理如下图:
slice 会取 x 和 y 中缩放比例较大的值,本例中
500/200=2.5
200/200=1
因此缩放比例为2.5,故对 viewBox 的大小进行缩放,viewBox 宽高均缩放为500
图形大小与相对坐标也按照2.5的比例进行缩放,因此绘制于图中位置
阴影部分超出了 viewport 区域因此不会显示
最后附上MDN链接:https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute/preserveAspectRatio
4 - svg 组件库最佳实践
在 vue 工程中创建一个组件叫做 SvgIcons
把上面的 svg 矩形复制到组件中稍作修改
组件代码如下:
<!-- src/components/SvgIcons/SvgIcons.vue -->
<template>
<svg width="500" height="200" viewBox="0 0 50 20" style="border: 1px solid red">
<rect x="10" y="10" width="10" height="5" stroke="green" fill="none" />
</svg>
</template>
<script>
export default {
name: 'SvgIcons'
}
</script>
在 App.vue 中使用
<!-- src/App.vue -->
<template>
<div id="app">
<svg-icons></svg-icons>
</div>
</template>
<script>
import SvgIcons from './components/SvgIcons.vue'
export default {
name: 'App',
components: {
SvgIcons
}
}
</script>
很简单就完成了 svg 组件的使用,然而实际上 svg 组件库的封装并没有这么简单
svg组件库
svg官方为我们提供了一些标签:
- defs
用于定义以后需要重复使用的图形 - g
将图形进行分组 - use
用于取出要使用的图形 - symbol
与 g 标签类似,但图形开发人员可以指定 viewBox 属性
用于帮助我们完成组件库的封装
首先,编写一些 svg 图标作为 svg 组件
修改 SvgIcons 组件
<!-- src/components/SvgIcons/SvgIcons.vue -->
<template>
<svg width="0" height="0" viewBox="0 0 100 100">
<defs>
<!-- 一个类似菜单按钮的图标 -->
<g id="menu">
<circle r="5" cx="20" cy="25" fill="currentColor" />
<circle r="5" cx="20" cy="50" fill="currentColor" />
<circle r="5" cx="20" cy="75" fill="currentColor" />
<line x1="40" y1="25" x2="90" y2="25" stroke-width="8" stroke="currentColor" />
<line x1="40" y1="50" x2="90" y2="50" stroke-width="8" stroke="currentColor" />
<line x1="40" y1="75" x2="90" y2="75" stroke-width="8" stroke="currentColor" />
</g>
</defs>
</svg>
</template>
<script>
export default {
name: 'SvgIcons'
}
</script>
修改 App.vue
<!-- src/App.vue -->
<template>
<div id="app">
<svg-icons></svg-icons>
<svg width="30" height="30">
<use href="#menu" />
</svg>
</div>
</template>
<script>
import SvgIcons from './components/SvgIcons.vue'
export default {
name: 'App',
components: {
SvgIcons
}
}
可以看到 svg 图标显示出来了,但只显示了一部分,这是因为 g
标签中是没有指定 viewBox 的,而最外层使用的 svg 也没有指定 viewBox,因此显示区域的大小与 viewport 相同为 30x30,图标实际大小为 100x100
在 App.vue 的 svg 加上 viewBox="0 0 100 100"
即可正常显示
由此可见 g
标签的局限,外层的 svg 必须指定 viewBox,但是我们作为图标的使用者,往往是不知道 viewBox 大小的,尤其是当使用一些来自外部的图标的时候,此时使用图标还需要阅读图标的源码就非常麻烦。
为了解决这个问题,出现了 symbol
标签,官方建议我们所有的 svg 图标都用 symbol
进行封装。
修改 SvgIcons.vue 的 g 标签为 symbol 标签,并加上 viewBox="0 0 100 100"
删除 App.vue 的 svg 上的 viewBox 属性
增加一些图标
最终代码如下
<!-- SvgIcons.vue -->
<template>
<svg width="0" height="0" viewBox="0 0 100 100">
<defs>
<!-- 一个类似菜单按钮的图标 -->
<symbol id="menu" viewBox="0 0 100 100">
<circle r="5" cx="20" cy="25" fill="currentColor" />
<circle r="5" cx="20" cy="50" fill="currentColor" />
<circle r="5" cx="20" cy="75" fill="currentColor" />
<line x1="40" y1="25" x2="90" y2="25" stroke-width="8" stroke="currentColor" />
<line x1="40" y1="50" x2="90" y2="50" stroke-width="8" stroke="currentColor" />
<line x1="40" y1="75" x2="90" y2="75" stroke-width="8" stroke="currentColor" />
</symbol>
<!-- 填充的右箭头 -->
<symbol id="filledArrowRight" viewBox="0 0 100 100">
<polyline points="20 10, 80 50, 20 90" fill="currentColor" />
</symbol>
<!-- 右箭头 -->
<symbol id="arrowRight" viewBox="0 0 100 100">
<polyline points="30 20, 70 50, 30 80" fill="transparent" stroke="currentColor" stroke-width="3" />
</symbol>
</defs>
</svg>
</template>
<script>
export default {
name: 'SvgIcons'
}
</script>
<!-- App.vue -->
<template>
<div id="app">
<!-- 以组件形式引入图标 -->
<svg-icons></svg-icons>
<svg width="30" height="30">
<use href="#menu" />
</svg>
<svg width="50" height="50" style="color: green">
<use href="#filledArrowRight" />
</svg>
<svg width="50" height="50" style="color: grey">
<use href="#arrowRight" />
</svg>
</div>
</template>
<script>
import SvgIcons from './components/SvgIcons.vue'
export default {
name: 'App',
components: {
SvgIcons
}
}
</script>
5 - 通用 svg 组件封装
经过对 svg 组件库的编写,在使用 svg 图标的时候会写很多类似 <svg ...><use ... /></svg>
的代码,可以将其封装为组件。
创建一个 Icon 组件
创建 Icon.vue 组件
<!-- src/components/Icon/Icon.vue -->
<template>
<svg :style="{...style}">
<use :href="iconName"></use>
</svg>
</template>
<script>
export default {
name: 'Icon',
props: {
name: String,
style: Object
}
setup(ctx) {
const iconName = `#${ctx.name}`
return {
iconName
}
}
}
在 App.vue 中使用
<template>
<div id="app">
<!-- 以组件形式引入图标 -->
<svg-icons></svg-icons>
<icon name="arrowRight" :style="{width: '30px', height: '30px'}" />
<icon name="menu" class="common-icon" />
</div>
</template>
<script>
import SvgIcons from './components/SvgIcons'
import Icon from './components/Icon'
export default {
name: 'App',
components: {
SvgIcons,
Icon
}
}
</script>
<style scoped>
.common-icon {
width: 50px;
height: 50px;
color: red;
}
</style>
6 - iconfont最佳实践
- 在 iconfont 上新建一个项目
- 搜索需要的图标添加入购物车
- 将购物车中的图标加入项目
- 对于有的图标自带颜色,而你不需要的情况,可以先将图标下载到本地,然后上传至项目,选择
去除颜色并提交
- 使用官方推荐的 Symbol 方式生成图标,对于测试使用可以直接将生成的链接放入
public.html
中,通过js引入
<script src="//at.alicdn.com/t/c/font_4436236_b4vj4cc2vy7.js"></script>
- 加入通用css代码(全局引入一次即可)
修改 Icon 组件
代码修改如下
<!-- src/components/Icon/Icon.vue -->
<template>
<div class="icon-wrapper" :style="{...style}">
<svg class="icon">
<use :href="iconName"></use>
</svg>
</div>
</template>
<script>
export default {
name: 'Icon',
props: {
name: String,
style: Object
}
setup(ctx) {
const iconName = `#${ctx.name}`
return {
iconName
}
}
}
</script>
<style type="text/css">
.icon-wrapper {
display: inline-block;
}
.icon {
width: 100%;
height: 100%;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
- 在 Icon 组件中根据图标名称进行使用
<icon name="icon-scan" class="common-icon" />
以上就是 iconfont 最近实践,快去试一试吧~