SVG 图形可以像 HTML 那样手动编写 ……
SVG 是使用 XML 来描述二维图形和绘图程序的语言,也就是使用 XML 格式定义的可伸缩矢量图形。它的主要应用场景是用来绘制 icon 和绘制动画。
对于 Web 而言,与其他图像格式相比,使用 SVG 的主要优势在于:
- SVG 与 JPEG、GIF 等图像比起来,尺寸更小,且可压缩性更强。
- SVG 是矢量图,可以伸缩,适用各种分辨率
- SVG 文件是纯粹的 XML,图像中的文本是可选的,同时也是可搜索的(很适合制作地图)
- SVG 可以与 CSS 结合,获得更强的扩展性;并可设置 class,通过 class 赋予 css 样式
一 基本概念及标签
1. viewport / viewBox / preserveAspectRatio
示例代码如:
<svg width="400" height="100" viewBox="0 0 40 10" style="border: 1px solid #000000">
<rect x="10" y="4" width="10" height="5" style="stroke: #000000; fill:none;"/>
</svg>
效果:
viewport 是 svg 图像的可见区域,也就是 <svg width="400" height="100"> 中指定的 width / height。
viewBox 是用于在画布上绘制 svg 图形的坐标系统。svg 绘图并不是按照 viewport 来绘制的,而是按照 viewBox 绘制,也就是说 viewport 是一个容器,而 viewBox 才是画板。
绘制的具体内容会按照 viewport 与 viewBox 的比值放大或缩小。 如上示例等同于:
<svg width="400" height="100" viewBox="0 0 400 100" style="border: 1px solid #000000">
<rect x="100" y="40" width="100" height="40" style="stroke: #000000; fill:none;"/>
</svg>
如果 viewBox 不指定,默认与 viewport 相同, 即:
<!-- 两者相同 -->
<svg width="400" height="100">
<svg width="400" height="100" viewBox="0 0 400 100">。
preserveAspectRatio 用于当 viewport 和 viewBox 宽高比不相同时,指定这个坐标系在viewport 中是否完全可见,同时也可以指定它在viewport 坐标系统中的位置。
viewport 和 viewBox 宽高比不相同时, viewBox 的缩放会不按比例强制拉伸,导致图像变形,preserveAspectRatio 就是用来设置 viewBox 在拉伸时需要保持的宽高比。
preserveAspectRatio 语法:
preserveAspectRatio=<align> <meetOrSlice>
align 参数控制 viewBox 是否强制进行均匀的缩放,主要控制 viewBox 的位置,主要可分为 x、y 两个部分, 每个部分有三个值,分别是min、mid、max。
| 取值 | 说明 |
|---|---|
| xMin | viewBox的最小x值对齐viewport的左边 |
| xMid | viewBox的x轴中点对齐viewport的x轴中点 |
| xMax | viewBox的最大x值对齐viewport的右边 |
| YMin | viewBox的最小y值对齐viewport的顶边 |
| YMid | viewBox的Y轴中点对齐viewport的Y轴中点 |
| YMax | viewBox的最大y值对齐viewport的底边 |
meetOrSlice 参数的取值:
| 取值 | 说明 |
|---|---|
| meet | 保持宽高比并将viewBox缩放为适合viewport的大小 |
| slice | 保持宽高比并将所有不在viewport中的viewBox剪裁掉 |
| none | 不保存宽高比。缩放图像适合整个viewBox,可能会发生图像变形 |
示例:
<svg width="400" height="100" viewBox="0 0 40 10" preserveAspectRatio="xMidYMid meet" style="border: 1px solid #000000">
<rect x="10" y="4" width="10" height="5" style="stroke: #000000; fill:none;"/>
</svg>
2. 常用形状标签及属性
常用标签:
| 标签 | 说明 |
|---|---|
| rect | 用来创建矩形,以及矩形的变种 |
| circle | 圆, cx / cy 指定圆点的 x 和 y 坐标, 默认均为0;r 指定圆半径 |
| ellipse | 椭圆 |
| line | 线条, 属性 x1 / y1 / x2 / y2 分别定义线条的开始与结束 |
| polygon | 不少于三个边的图形, 属性 points 定义多边形每个角的 x 和 y 坐标 |
| polyline | 折线,仅包含直线的形状,用 points 属性定义 |
| path | 路径 |
常用属性:
| 标签属性 | 说明 |
|---|---|
| x | 图像距离左侧的位置 |
| y | 图像距离顶端的位置 |
| rx / ry | 水平半径 / 垂直半径(椭圆,或者矩形的圆角) |
| width | 图像宽 |
| height | 图像高 |
| style | 用来定义 CSS 属性 |
对于动画属性见下文。
style除一般 css 属性外,有以下几个常用属性,这些属性也可以直接写在标签内:
| style属性 | 说明 |
|---|---|
| fill | 填充颜色 |
| fill-opacity | 填充颜色透明度 |
| stroke | 边框的颜色 |
| stroke-width | 边框的宽度 |
| stroke-opacity | 边框的颜色透明度 |
| filter | 滤镜 |
对于<path>标签,指定路径的参数比较复杂,对于复杂的图形,建议使用 SVG 编辑器来创建:
// 所有命令均允许小写字母。大写表示绝对定位,小写表示相对定位。
M = moveto
L = lineto
H = horizontal lineto
V = vertical lineto
C = curveto
S = smooth curveto
Q = quadratic Belzier curve
T = smooth quadratic Belzier curveto
A = elliptical Arc
Z = closepath
示例如:
<!-- 开始于位置 250 150,到达位置 150 350,然后从那里开始到 350 350,最后在 250 150 关闭路径 -->
<svg width="100%" height="100%">
<path d="M250 150 L150 350 L350 350 Z" />
</svg>
二 常用组件库及实践
| 组件库 | 说明 |
|---|---|
| defs | 用于嵌入可在 SVG 映像内重用的定义。在<defs>元素中定义的形状不会显示在SVG图像中,须被<use>元素引用才能显示 |
| g | 将 SVG 形状分组在一起,分组后,您可以像变形单个形状一样变换整个形状。在引用 g 元素之前,须设置其 id |
| use | 复用 SVG 文档中其他位置的 SVG 图形,通过 x 和 y 属性指定在何处显示 |
| symbol | 与 g 标签类似,但 symbol 可以拥有一个独立的 viewBox,以使图形的复用更灵活方便 |
示例:
<!-- 图形集合 -->
<svg viewBox="0 0 100 100">
<defs>
<g id="test">
<circle r="5" cx="20" cy="25" fill="red" />
<circle r="5" cx="20" cy="50" fill="blue" />
</g>
<g id="test2">
<line x1="40" y1="25" x2="90" y2="25" stroke-width="8" stroke="blue">
</g>
<symbol id="test3">
<line x1="40" y1="25" x2="90" y2="25" stroke-width="8" stroke="blue">
</symbol>
</defs>
</svg>
<!-- 可在另一个文件中引入定义的图形, use 标签的 href 须指向定义的 id -->
<svg width="30" height="30" viewBox="0 0 100 100" style="color: yellow">
<use herf="#test"></use>
</svg>
<!-- 如果图形使用symbol标签定义的,则引用时不需要再次指定 viewBox -->
<svg width="30" height="30" style="color: yellow">
<use herf="#test3"></use>
</svg>
Vue 封装 svg 组件示例:
// 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,
prefix: {
type: String,
default: 'icon'
},
style: Object
},
setup(ctx) {
const iconName = `#${ctx.prefix}${ctx.name}`
return {
iconName
}
}
}
</script>
// 导出 Icon 组件 并全局注册: Icon/index.js
import Icon from './Icon.vue'
export default function(Vue) {
Vue.component(Icon.name, Icon)
}
// 入口文件use
import Icon from './components/Icon/index'
export default function(Vue) {
Vue.use(Icon)
}
// 后续导入svg 库,便可以使用
<Icon name="xxx" :style="{}"></Icon>
对于 svg 库,可自行在Iconfont 创建(建议选择symbol方式),生成 js 文件,然后全局引入;或者复制 svg 内容粘贴到本地 defs 标签内然后引用。
三 svg 动画
1.transform 变换
与 css 的 transform 类似, 结合 keyframes 与 animation 实现动画:
| transform 属性 | 说明 |
|---|---|
| translate | 平移 |
| rotate | 旋转 |
| skewX / skewY | 斜切 |
| scale | 缩放 |
| matrix | 复杂变形(矩阵) |
matrix 可以实现其他四种的所有功能,matrix 的参数分为两组,六个数值,其中一三五一组,二四六一组,其坐标换算如:
<svg width="500" height="200" viewBox="0 0 500 200">
<rect x="0" y="0" width="100" height="50" transform="matrix(2,1,-1,2,50,0)">
</svg>
第一组数值为 firstArr = [2, -1, 50]; 第二组数值为 secondArr = [1,2,0]。
上述代码为一个矩形,其四个顶点坐标为:[0,0], [100,0], [100,50], [0,50]。
matrix 根据旧坐标换算出新坐标,换算公式为:
newX = firstArr[0] * oldX + firstArr[1] * oldY + firstArr[2]
newY = secondArr[0] * oldX + secondArr[1] * oldY + secondArr[2]
得到的结果为:[50,0], [250,100], [200, 200], [0, 100],根据新的坐标画出新图形。
对于其他图形也是如此,找四个点的坐标进行换算,也可以通过新旧坐标反算出 matrix。如:
得出 a, b, c, d, e, f 的结果为:matrix(0,-1,1,0,0,440)
示例:
<div>
<svg width="220" height="220" viewbox="0 0 220 220">
<circle cx="110" cy="110" r="85" stroke-width="25" stroke="#D1D3D7" fill="none"></circle>
<circle
class="circle"
cx="110"
cy="110"
r="85"
stroke-width="25"
stroke="#00A5E0"
fill="none"
transform="matrix(0,-1,1,0,0,220)"
/>
</svg>
</div>
<style>
.circle {
animation: circle 5s linear infinite;
}
@keyframes circle {
from {
stroke-dasharray: 0 1069;
}
to {
stroke-dasharray: 1069 0;
}
}
</style>
注:stroke-dasharray, stroke-dashoffset
2. SMIL
SMIL 允许我们通过 HTML 标签实现动画效果,包含以下标签:
| SMIL 标签 | 说明 |
|---|---|
| set | 设置一个属性值指定时间 |
| animate | 随时间动态改变属性 |
| animateColor | 随着时间的推移颜色转换(已废弃,可通过animate实现) |
| animateTransform | 动画上一个目标元素变换属性,从而使动画控制平移,缩放,旋转或倾斜放 |
| animateMotion | 使元素沿着动作路径移动 |
标签内的常用属性:
| 属性 | 说明 | |||
|---|---|---|---|---|
| attributeName | 要变化的元素属性名称,可以是 SVG 标签上的属性,如width/height,也可以是CSS属性,如opacity | |||
| attributeType | 三个固定参数 CSS / XML / auto, 用来表明定义在 attributeName 上面的属性 | |||
| from | 动画的起始值 | |||
| to | 动画的结束值 | |||
| by | 动画的相对变化值(优先级低于 to) | |||
| values | 用分号分隔的一个或多个值,可以看出是动画的多个关键值点(优先级高于from/to/by) | |||
| begin | 动画开始的时间, 具体时间值 ‘h’ | ‘min’ | ‘s’ | ‘ms’这些,默认单位是’s’, 也可是相对时间如:xx.end/circle.click 等。 |
| end | 动画结束的时间 | |||
| dur | 动画执行的时长 | |||
| calcMode, keyTimes, keySplines | 设置动画执行的快慢 | |||
| repeatCount | 动画执行次数 | |||
| repeatDur | 重复动画的总时间 | |||
| fill | 动画间隙的填充方式。支持参数有:freeze | remove. 其中remove是默认值,表示动画结束直接回到开始的地方。freeze“冻结”表示动画结束后像是被冻住了,元素保持了动画结束之后的状态 | ||
| accumulate | 支持参数有:none | sum. 默认值是none。如果值是sum表示动画结束时候的位置作为下次动画的起始位置 | ||
| additive | 动画是否附加。支持参数有:replace | sum. 默认值是replace。如果值是sum表示动画的基础知识会附加到其他低优先级的动画上 | ||
| restart | 动画是否可以重复执行。可设置的值为always/whenNotActive/never | |||
| min / max | 执行的最短和最长时间 |
SVG 提供一些js接口可以用于控制动画, 如 svg.pauseAnimations()(暂停),svg.unpauseAnimations()(重启)
示例如(以下代码可正常演示,本地 Markdown 环境好像不支持动画的演示):
<svg width="200" height="200">
<rect x="0" y="0" width="100" height="100" fill="red">
<set attributeName="x" attributeType="XML" to="10" begin="1s" />
<set attributeName="x" attributeType="XML" to="20" begin="2s" />
</rect>
</svg>
<svg width="200" height="200" viewBox="0 0 20 20">
<circle cx="0" cy="0" r="3" fill="blue" stroke="black" stroke-width="0.1">
<animate attributeName="cx" from="0" to="200" dur="5s" repeatCount="indefinite" />
<animate attributeName="cy" from="0" to="200" dur="5s" repeatCount="indefinite" />
</circle>
</svg>
<svg width="200" height="200" viewBox="0 0 200 200">
<rect x="0" y="0" width="60" height="60" fill="red">
<animateTransform attributeName="transform" begin="0s" dur="3s" type="scale" from="1" to="2" repeatCount="indefinite" />
</rect>
</svg>
animateMotion: 按 path 轨迹运动的正方形
<svg width="200" height="200" viewBox="0 0 200 200">
<rect x="0" y="0" width="10" height="10" fill="red">
<animateMotion
path="M 10 10 L 110 10 L 110 110 L 10 110 Z"
dur="5s"
rotate="auto"
fill="freeze"
repeatCount="indefinite"
/>
</rect>
<path id="motion-path" d="M 10 10 L 110 10 L 110 110 L 10 110 Z" fill="none" stroke="green" />
</svg>
点击变色或位移:
<svg viewBox="0 0 200 200" width="200" height="200">
<g id="rect1">
<rect x="0" y="0" rx="0" ry="0" width="100" height="100" fill="red">
<animate
attributeType="XML"
attributeName="fill"
from="red"
to="green"
begin="rect1.click"
dur="2s"
fill="freeze"
/>
</rect>
</g>
<animateTransform
attributeType="XML"
attributeName="transform"
type="translate"
from="0, 0"
to="50, 50"
begin="rect1.click"
dur="2s"
fill="freeze"
/>
<rect x="0" y="100" width="100" height="100" fill="blue">
<animate
attributeType="XML"
attributeName="fill"
from="blue"
to="green"
begin="rect1.click"
dur="2s"
fill="freeze"
/>
</rect>
</svg>
注:IE 不支持 SMIL。