svg及动画

目标:

示例1:希望①旋转并②波纹扩散效果。

示例2:希望重新定义仪表盘组件。即根据数值,显示圆形进度条。

实现SVG动画的三种方式

方式1:SMIL语言

特点

  1. 用SVG元素标签来描述动画。
  2. 仅嵌入SVG使用。
  3. Synchronized Multimedia Integration Language(同步多媒体集成语言)。

动画标签

<set> 瞬间动画。虽然没有动画过度效果。但可实现延迟功能,类似setTimeout。
<animate> 用于实现单属性的动画效果。
<animateColor> 颜色发生动画效果(被animate替代)
<animateTransform> 变形类动画
<animateMotion>沿着某个路径运动

各标签自由组合,作用同一个元素。

标签的位置
位置1,作为需要动画SVG元素标签的子元素。
位置2,放到任意位置,只需要利用xlink:href = ‘#id’ 做关联即可,需要做如下配置:

方式2:CSS3

特点

  1. 用css样式描述动画。
  2. 嵌入SVG或外部css文件均可。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 638 458">

<style type="text/css">
#innerCircle, #outerCircle, #middleCircle {
animation: process 3s linear infinite;
transform-origin: 25% 25%;
}
@keyframes process {
100% {
transform: scale(1.1);
}
}
</style>

<!--其他SVG元素-->
<defs />
<g id="innerCircle" style="stop-color:#2D80FF" />
</svg>

方式3:JavaScript

特点

  1. 直接用js来操作svg(dom)。
  2. 利用第三方js库操作svg(dom):Snap.svg、Velocity.js、anime.js等。
  3. 单独的js文件。
  4. Snap.svg是使用比较多的库。但是引用起来有点费事。
  5. 循环动画,需要借助js setInterval来实现。
  6. 如果只是简单的动图,没必要使用js。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
npm install snapsvg

import Snap from 'imports-loader?this=>window,fix=>module.exports=0!snapsvg/dist/snap.svg.js'

// 使用
const svg = Snap('#svg');
svg.paper.circle({
cx: 100,
cy: 100,
r: 50,
fill: '#f00',
});
svg
.select('circle')
.animate(
{r: 100},
1000,
mina.easeout(),
function() {console.log('animation end');});

SVG动画的三种重要种类

核心概念

svg动画的核心概念是路径path。path就是一条线,是直线,或者曲线,虚线或者实线。一个path长这样【请学习svg基础】——

M113,318c-50.7-1.8-74.3-24.8-81-32C6.7,258.7,8.1,224.2,11,155z

一个简单path标签长这样——

<path d="M113,318c-50.7-1.8-74.3-24.8-81-32C6.7,258.7,8.1,224.2,11,155z" />

path具有如下线的属性:

  1. stroke:颜色,stroke=”green”。
  2. d:线条的形状。
  3. stroke-width:宽度,单位是 px。
  4. stroke-dasharray:定义形成一条虚线。stroke-dasharray=”5, 15” 表示,按照 实线dash为 5,间隔gap为 15 的排布重复下去。值可以为 number || percentage。
  5. stroke-dashoffset:设置 dasharray 定义的 dash 线条开始的位置。值可以为 number || percentage。
  6. stroke-linecap: 线条的端点样式。
  7. stroke-linejoin: 线条连接的样式

SVG预定义的六种基本形状: rect、circle、ellipse、line、polyline、polygon 都可以转换为path路径,从而实现动画。这篇文章提供了转换函数https://aotu.io/notes/2017/01/16/base-shapes-to-path/index.html

类型1:描边动画/划线动画

参见下文示例2的实现。

实现方式
  1. 方式1:【推荐】css3的stroke-dasharray和stroke-dashoffset。
  2. 方式2:SMIL的 <animate> 标签。
具体实现

描边动画有个核心问题:获取边的总长度。比如示例2中,需要获得圆盘的总长度。因为svg本身就是dom元素,可用js获取:document.getElementById('路径path的id').getTotalLength()

CSS3

css3实现实线描边有两种方式,假设获得总长度369px。

方式一、利用stroke-dasharray和stroke-dashoffset。调整线的偏移量即可。

1
2
3
4
5
6
7
8
#path1 {
animation: process1 3s linear forwards;
stroke-dasharray: 369px, 369px;
}
@keyframes process1 {
0% { stroke-dashoffset: 369px;}
100% { stroke-dashoffset: 0; }
}

方式二、纯利用stroke-dasharray。调整实线dash的大小即可。

1
2
3
4
5
#path { animation: process 3s linear forwards; }
@keyframes process {
0%{ stroke-dasharray: 0, 369px;}
100%{ stroke-dasharray: 369px, 369px; }
}
animate

<animate>可以通过改变样式属性来处理。

1
2
3
4
5
6
7
8
<animate
attributeName="stroke-dashoffset"
begin="0.8"
dur='0.3s'
from='369.59027099609375px'
to='0px'
fill="freeze"
/>

类型2:路径动画

让元素沿着某条路径进行运动。参见下文示例1的实现。

实现方式
  1. 方式1:【推荐】SMIL的 <animateMotion>标签。
  2. 方式2:CSS3 的offset-path和offset-distance样式。

css3虽然操作顺手,但浏览器兼容性没有<animateMotion>好。而且,因为path本身是svg内部的元素,而路径轨迹动画本身依赖于路径。因此直接<animateMotion>比css更稳定方便。

具体实现
animateMotion
1
2
3
4
5
6
7
8
9
10
11
12
<g>
<rect x="0" y="0" width="50" height="30" style="fill: #ccc;"/>
<circle cx="40" cy="30" r="10" style="fill: #cfc; stroke: green;"/>
<animateMotion path="M50,125 C 100,25 150,225, 200, 125" dur="4s"/>
</g>

或,预定义一个path,而后引用:

<defs><path id="cubicCurve" d="M50,125 C 100,25 150,225, 200, 125"/></defs>
<animateMotion dur="4s" fill="freeze" rotate="auto">
<mpath xlink:href="#cubicCurve" />
</animateMotion>
css3
1
2
3
4
5
6
7
8
9
10
11
12
13
.ball {
offset-path: path('M10 80 Q 77.5 10, 145 80 T 280 80');
offset-distance: 0%;
animation: red-ball 2s linear alternate infinite;
}
@keyframes red-ball {
from {
offset-distance: 0%;
}
to {
offset-distance: 100%;
}
}

类型3:变形动画

实现方式
  1. 方式1:【推荐】CSS3 的transform。
  2. 方式2:SMIL的 animateTransform。

两种方法都有如下属性:

  • translate
  • rotate
  • scale
  • skew
具体实现

在css和svg本身的属性中,都具备这几种变形。但是,他们坐标系不同。

  • css以元素本身的中心点作为坐标原点进行变形。并且css可以通过transform-origin自定义原点。
  • svg以整个svg的左上角(0,0)为原点变形。当执行svg的scale时,x,y的位置也会跟着缩放。
animateTransform
1
2
3
4
5
6
7
8
9
<animateTransform
id='a1'
attributeName="transform"
type="scale"
from="1"
to="2"
dur="5s"
fill="freeze"
/>
css3
1
2
3
4
5
6
7
8
9
#innerCircle, #outerCircle, #middleCircle {
animation: process 3s linear infinite;
transform-origin: 25% 25%;
}
@keyframes process {
100% {
transform: scale(1.1);
}
}

目标示例的具体实现

  1. 静态SVG图片本身由UI利用sketch或者Adobe Illustrator工具绘制。
  2. 动态SVG效果,由开发人员利用代码自行处理。
  3. 动画所沿路径path也可以由UI利用工具帮助勾勒。

示例1:①旋转并②波纹扩散

波纹:css的scale变换。变换动画。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 静态波纹本身,由UI提供
<g id="innerCircle"></g>
<g id="outerCircle"></g>
<g id="middleCircle"></g>

// 设置动画
#innerCircle, #outerCircle, #middleCircle {
animation: process 3s linear infinite;
transform-origin: 25% 25%;
}
@keyframes process {
100% {
transform: scale(1.1);
}
}
运动:animateMotion标签。路径动画。
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
// 三个棋子要走的路线path,可由UI工具勾勒
<defs>
<path id="cubicCurve1" d="Mxxxxz"/>
<path id="cubicCurve2" d="Mxxxxz"/>
<path id="cubicCurve3" d="Mxxxxz"/>
</defs>

<g id="棋子1">
/** 静态棋子本身代码,由UI提供**/

// 设置动画,即该棋子走的路线path
<animateMotion dur="20s" repeatCount="indefinite">
<mpath xlink:href="#cubicCurve1" />
</animateMotion>
</g>

<g id="棋子2">
<animateMotion dur="20s" repeatCount="indefinite">
<mpath xlink:href="#cubicCurve2" />
</animateMotion>
</g>

<g id="棋子3">
<animateMotion dur="20s" repeatCount="indefinite">
<mpath xlink:href="#cubicCurve3" />
</animateMotion>
</g>

示例2:定义仪表盘组件。根据数值,显示圆形进度条。

此图利用stroke-dasharray实现。描边动画。

注意,总长度可由document.getElementById('路径path的id').getTotalLength() 取得。

此处为:document.getElementById(‘进度条高亮’).getTotalLength() = 365

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 定义百分比   
const value = (365 * percentFormat) / 100;

// 利用 value + stroke-dasharray 实现百分比效果
<path
id="进度条高亮"
d="Mxxxxx"

style={{ strokeDasharray: `${value}px, 365px`, strokeDashoffset: '-2.5px' }}

stroke="url(#linearGradient-10)"
strokeWidth="22"
fillOpacity="0"
/>
-------------Keep It Simple Stupid-------------
0%