Appearance
这是 A Guide to SVG Animations (SMIL) 的部分内容4,关于沿路径动画。
使用 path
特性指定运动轨迹
📚 动画元素上的 path
特性用于指定运动轨迹,它的语法和 d
一样:
- 运动路径动画的效果是在被引用对象的当前转换矩阵上添加一个补充转换矩阵,该转换矩阵通过随时间计算的X和Y值沿当前用户坐标系的X和Y轴进行平移。
- 换句话说,指定的路径是相对于元素的当前位置计算的,通过使用路径数据将元素转换为路径位置。
🌰 比如某个圆沿着特定路径动画:
html
<animateMotion
xlink:href="#circle"
dur="1s"
begin="begin"
fill="freeze"
path="M0,0c3.2-3.4,18.4-0.6,23.4-0.6c5.7,0.1,10.8,0.9,16.3,2.3c13.5,3.5,26.1,9.6,38.5,16.2c12.3,6.5,21.3,16.8,31.9,25.4c10.8,8.7,21,18.3,31.7,26.9c9.3,7.4,20.9,11.5,31.4,16.7c13.7,6.8,26.8,9.7,41.8,9c21.4-1,40.8-3.7,61.3-10.4c10.9-3.5,18.9-11.3,28.5-17.8c5.4-3.7,10.4-6.7,14.8-11.5c1.9-2.1,3.7-5.5,6.5-6.5"
/>
WARNING
值得注意的是,上面的 path
的 M0,0
中的起始点 (0,0)
表示的是 circle
的位置,而不是SVG viewport坐标系左上角的位置。
使用mpath 元素引用路径
除了上面 <animateMotion path="xxx" />
方式外,还可以使用 <animateMotion><mpath xlink:href="#somet_path"/></animateMotion>
引用外部路径:
html
<animateMotion xlink:href="#circle" dur="1s" begin="click" fill="freeze">
<mpath xlink:href="#motionPath"></mpath>
</animateMotion>
这种方式的特点:
- 运动路径可以定义在文档的任何位置,它可以定义在元素内,但是不在画布上进行渲染
- 这种情况和上面用法的不同之处在于,运动的元素需要设置为
(0,0)
位置,否则会出现跳动现象,因为元素已经被转换过了
🌰 没有将圆的 cx
& cy
设置为 (0,0)
,动画开始时,圆突然往下跳动:注意这里的path并不是从 M0,0 开始的
html
<svg width="500" height="350" viewBox="0 0 500 350">
<path id="motionPath" fill="none" stroke="#000000" stroke-miterlimit="10" d="M91.4,104.2c3.2-3.4,18.4-0.6,23.4-0.6c5.7,0.1,10.8,0.9,16.3,2.3
c13.5,3.5,26.1,9.6,38.5,16.2c12.3,6.5,21.3,16.8,31.9,25.4c10.8,8.7,21,18.3,31.7,26.9c9.3,7.4,20.9,11.5,31.4,16.7
c13.7,6.8,26.8,9.7,41.8,9c21.4-1,40.8-3.7,61.3-10.4c10.9-3.5,18.9-11.3,28.5-17.8c5.4-3.7,10.4-6.7,14.8-11.5
c1.9-2.1,3.7-5.5,6.5-6.5"/>
<circle id="circle" r="20" cx="100" cy="100" fill="tomato" />
<animateMotion
xlink:href="#circle"
dur="1s"
begin="click"
fill="freeze">
<mpath xlink:href="#motionPath" />
</animateMotion>
</svg>
<p>Click on the circle to animate it.</p>
animateMotion + mpath元素 + 引用外部路径 - @codepen 异常版本
🧑🔧为了修复这个问题,将 circle
元素的 cx
& cy
均设置为 0
,即:
html
<svg width="500" height="350" viewBox="0 0 500 350">
<path id="motionPath" fill="none" stroke="#000000" stroke-miterlimit="10" d="M91.4,104.2c3.2-3.4,18.4-0.6,23.4-0.6c5.7,0.1,10.8,0.9,16.3,2.3
c13.5,3.5,26.1,9.6,38.5,16.2c12.3,6.5,21.3,16.8,31.9,25.4c10.8,8.7,21,18.3,31.7,26.9c9.3,7.4,20.9,11.5,31.4,16.7
c13.7,6.8,26.8,9.7,41.8,9c21.4-1,40.8-3.7,61.3-10.4c10.9-3.5,18.9-11.3,28.5-17.8c5.4-3.7,10.4-6.7,14.8-11.5
c1.9-2.1,3.7-5.5,6.5-6.5"/>
<circle id="circle" r="20" cx="100" cy="100" fill="tomato" />
<circle id="circle" r="20" cx="0" cy="0" fill="tomato" />
<animateMotion
xlink:href="#circle"
dur="1s"
begin="0s"
fill="freeze">
<mpath xlink:href="#motionPath" />
</animateMotion>
</svg>
<p>Click on the circle to animate it.</p>
🌰小圆围绕圆圈路径运动的示例:小球绕圆形路径运动
animtionMotion 特性优先级
<animateMotion>
可以定义多种动画方式,如果同时添加这些规则,势必存在某种覆盖另一种的情形:
- 关于运动路径的定义,
mpath
元素覆盖了path
特性,path
特性覆盖了value
,value
覆盖了from, by & to
- 关于确定与
keyTimes
属性对应的点,keyPoints
属性覆盖path
,path
覆盖value
,value
覆盖from、by & to
⭐ 元素沿着路径的旋转方向 - rotate
上面的例子都是使用 圆
作为示例,它是对称的,但如果是一些不对称的物体沿着路径动画,就可能出现很奇怪的效果。比如下面这辆小车:
html
<svg width="500" height="350" viewBox="0 0 500 350">
<path id="motionPath" fill="none" stroke="#000000" stroke-miterlimit="10" d="M202.4,58.3c-13.8,0.1-33.3,0.4-44.8,9.2
c-14,10.7-26.2,29.2-31.9,45.6c-7.8,22.2-13.5,48-3.5,70.2c12.8,28.2,47.1,43.6,68.8,63.6c19.6,18.1,43.4,26.1,69.5,29.4
c21.7,2.7,43.6,3.3,65.4,4.7c19.4,1.3,33.9-7.7,51.2-15.3c24.4-10.7,38.2-44,40.9-68.9c1.8-16.7,3.4-34.9-10.3-46.5
c-9.5-8-22.6-8.1-33.2-14.1c-13.7-7.7-27.4-17.2-39.7-26.8c-5.4-4.2-10.4-8.8-15.8-12.9c-4.5-3.5-8.1-8.3-13.2-11
c-6.2-3.3-14.3-5.4-20.9-8.2c-5-2.1-9.5-5.2-14.3-7.6c-6.5-3.3-12.1-7.4-19.3-8.9c-6-1.2-12.4-1.3-18.6-1.5
C222.5,59,212.5,57.8,202.4,58.3"/>
<g id="car" transform="translate(-234.4, -182.8)">
<path d="M234.4,182.8c-3.5,0-6.4,2.9-6.4,6.4c0,3.5,2.9,6.4,6.4,6.4c3.5,0,6.4-2.9,6.4-6.4C240.8,185.6,238,182.8,234.4,182.8z"/>
<circle cx="234.4" cy="189.2" r="2.8"/>
<path d="M263,182.8c-3.5,0-6.4,2.9-6.4,6.4c0,3.5,2.9,6.4,6.4,6.4c3.5,0,6.4-2.9,6.4-6.4C269.4,185.6,266.6,182.8,263,182.8z"/>
<circle cx="263" cy="189.2" r="2.8"/>
<path d="M275,171.4c-2.8-0.7-5.2-3-6.3-5.1l-3.9-7.4c-1.1-2.1-3.9-3.8-6.3-3.8h-22.6c-2.4,0-5,1.8-5.7,4.1l-2.4,7
c-0.2,0.9-1.8,5.5-5,5.5c-2.4,0-5,3.1-5,5.5v8.2c0,2.4,1.9,4.3,4.3,4.3h4.5c0-0.2,0-0.3,0-0.5c0-4.3,3.5-7.8,7.8-7.8
c4.3,0,7.8,3.5,7.8,7.8c0,0.2,0,0.3,0,0.5h13.1c0-0.2,0-0.3,0-0.5c0-4.3,3.5-7.8,7.8-7.8s7.8,3.5,7.8,7.8c0,0.2,0,0.3,0,0.5h8.1
c2.4,0,4.3-1.9,4.3-4.3v-6.5C283.2,172,277.3,172,275,171.4z"/>
<path d="M241.8,170.3h-12.5c0.7-1.1,1.1-2.2,1.2-2.6l2-5.9c0.6-1.9,2.8-3.5,4.8-3.5h4.5V170.3z"/>
<path d="M246.1,170.3v-12h10.4c2,0,4.4,1.5,5.3,3.3l3.3,6.3c0.4,0.8,1.1,1.7,2,2.4H246.1z"/>
</g>
<animateMotion
xlink:href="#car"
dur="3s"
begin="0s"
fill="freeze"
repeatCount="indefinite">
<mpath xlink:href="#motionPath" />
</animateMotion>
</svg>
因为小车的方向是固定的,没有随着路径的变化而变化,为了解决这个问题,需要使用 rotate
特性。
📚
rotate
特性有3个可选值:
auto
- 指示物体随时间按运动路径的方向(即方向切向量)的角度旋转。auto-reverse
- 表示物体随时间旋转的角度是运动路径的方向(即方向切向量)加上180度。某个数值
- 指示目标元素具有应用于它的恒定旋转转换,其中旋转角度为指定的度数。
下面使用 rotate
的值进行修复
🌰修复1:roate="auto"
小车沿着路径自动转向,沿着内圈逆时针
运动,但车头是反的 - rotate-auto
html
<animateMotion
xlink:href="#car"
dur="3s"
begin="0s"
fill="freeze"
rotate="auto"
repeatCount="indefinite">
<mpath xlink:href="#motionPath" />
</animateMotion>
🌰修复2:roate="auto-reverse"
小车沿着路径自动转向,沿着外圈逆时针
运动,但车头是反的 - rotate-auto-reverse
🌰🎉修复3:修复车头反向的问题,上面都是在开倒车,为了修复这个问题,需要将 <g id="car" transform="...">
进行反转,这里有一个小技巧,就是使用 scale(-1, 1)
对某个轴进行镜像:
html
<g id="car" transform="translate(-234.4, -182.8)">
<g id="car" transform="scale(-1, 1) translate(-234.4, -182.8)">
// ...car形状
</g>
使用keyPoints控制路径动画的动画距离
📚 keyPoints
特性提供了为每个keyTimes
指定值指定沿运动路径的进度的能力。如果指定了,keyPoints
将导致keyTimes
应用于keyPoints
中的值,而不是应用于values
特性数组中指定的点或path
特性上的点:
keyPoints
- 表示在相应的keyTimes
值指定的时刻,物体在运动路径上应该移动多远。0-1
之间的值列表,用;
分割,eg0.3;1;0;0.3
- 列表中的每个进度值都对应于
keyTimes
特性列表中的值; - 这也意味着
keyPoints
的数量一定要等于keyTimes
定义的数量
向前运动的定义的方式:
bash
keyPoints:
(startPosition)
1
0
(startPosition)
keyTimes:
0
(1 - startPosition)
(1 - startPosition)
1
反向运动:
bash
keyPoints:
(startPosition)
0
1
(startPosition)
keyTimes:
0
(startPosition)
(startPosition)
1
Motion along a closed Path, arbitrary start point - @codepen
⭐ 文本路径动画 - startOffset
可以使用 <textPath>
元素,将文字沿着指定的路径放置。
🌰
html
<svg>
<path id="myPath" d="..." />
<text>
<textPath xlink:href="#myPath">
Text laid out along a Path
</textPath>
</text>
</svg>
textPath引用路径,使文字沿指定路径摆放 - @codepen
为了创建文字沿着路径运动的动画,可以对 startOffset
特性进行动画:textPath + startOffset文字路径动画 - @codepen
html
<svg>
<path id="myPath" d="..." />
<text>
<textPath xlink:href="#myPath">
Text laid out along a Path
<animate
attributeName="startOffset"
from="0%"
to="100%"
begin="0s"
dur="5s"
repeatCount="indefinite"
keyTimes="0;1"
calcMode="spline"
keySplines="0.1 0.2 .22 1"
/>
</textPath>
</text>
</svg>
译者注:
关于路径动画,最简单的还是使用 GSAP MotionPathPlugin
扩展阅读:
2022年11月15日16:47:33