系列文章:
本文是本系列的最后一篇文章,主要来学习SVG的动画系统。温馨提示:本文虽然看上去不难,但是实际操作的时候,往往都会考验到之前的文章中的内容,毕竟SVG的动画也得应用到元素上才有效果,至少得有基本结构吧。
动画元素
<animate>
<animate>元素用于在SVG图形中定义属性的动态变化。通过指定起始值、结束值、持续时间和动画类型等实现各种各样的动画效果。
属性

🌰:
            
            
              html
              
              
            
          
          <svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
  <rect x="200" y="200" width="100" height="100" fill="lightblue">
    <animate
      attributeName="width"
      values="100;150;100"
      dur="5s"
      repeatCount="indefinite" />
  </rect>
</svg>
        可访问性问题
一些动画可能会对部分特殊人群产生负面影响(比如闪烁动画可能对患有注意力缺陷多动障碍的人群的影响是负面的)。因此尽量提供一种用户可以暂停或禁用动画的机制,为不喜欢动画体验的用户提供帮助。
<set>
<set>元素用于设置一个动画延迟,即动画何时变成活动状态。
注意:属性值的变化是突变的,没有平滑的过渡过程。
属性
<set>支持所有属性类型。通过在<set>元素上使用to属性和attributeName等属性配合使用定义要在动画持续时间内应用于目标属性的值。to属性的值必须与属性类型匹配。
🌰:
            
            
              html
              
              
            
          
          <!-- 1s后矩形颜色变成绿色 -->
<svg
  width="120"
  height="120"
  xmlns="http://www.w3.org/2000/svg">
  <rect height="100" width="100">
    <set
      attributeName="fill"
      to="green"
      begin="1s" />
  </rect>
</svg>
        <animateTransform>
<animateTransform>元素用于将目标元素上的transform属性动画化。
属性
| 属性名 | 值 | 简介 | 
|---|---|---|
type | 
translate/scale/rotate/skewX/skewY | 
指定要使其值随时间变化的转换类型。 | 
by | 
根据type属性的值而定 | 
指定将在动画过程中修改的属性的相对偏移值。 | 
from | 
根据type属性的值而定 | 
表示将在动画期间修改的属性的初始值。 | 
to | 
根据type属性的值而定 | 
表示将在动画期间修改的属性的最终值。 | 
by、from和to属性的值格式是一样的,具体根据type属性的值而定:
type="translate",值格式为:tx[, ty]。type="scale",值格式为:sx [,sy]。type="rotate",值格式为:旋转角度 [cx cy]。type="skewX"/type="skewY",值格式为:倾斜角度。
🌰:
            
            
              html
              
              
            
          
          <!-- 旋转 -->
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
  <rect x="200" y="200" width="100" height="100" fill="lightblue">
    <!-- 需要设置attributeName="transform" -->
    <animateTransform
      attributeName="transform"
      type="rotate"
      from="0 250 250"
      to="360 250 250"
      dur="5s" />
  </rect>
</svg>
        <animateMotion>与<mpath>
<animateMotion>元素定义元素如何沿指定的运动路径移动。
<mpath>子元素用于引用外部<path>元素作为运动路径。
属性
<animateMotion>属性
| 属性名 | 值 | 简介 | 
|---|---|---|
calcMode | 
discrete(离散)/linear(线性,默认)/paced(匀速)/spline(样条) | 
指定动画的插值模式。 | 
keyPoints | 
0 - 1 | 
对象与每个时间键关联值的路径距离。 | 
path | 
前缀表示法 | 使用与d属性相同的语法定义运动的路径。 | 
rotate | 
numebr/auto/auto-reverse | 
定义应用于沿路径动画的元素的旋转,通常使其指向动画的方向。 | 
origin | 
default | 
指定动画的起点。仅在additive="replace"时应用。 | 
注意:
<mpath>会覆盖path属性。
🌰:
            
            
              html
              
              
            
          
          <svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
  <path
    id="path1"
    d="M 50 50 A 50 50 0 1 1 50 200 Z"
    fill="none"
    stroke="lightpink"
    stroke-width="3" />
  <circle cx="0" cy="0" r="20" fill="lightblue">
    <animateMotion dur="5s" repeatCount="indefinite" rotate="auto">
      <mpath href="#path1" />
    </animateMotion>
    <!-- 或者 -->
    <!-- <animateMotion dur="5s" path="d="M 50 50 A 50 50 0 1 1 50 200 Z"" repeatCount="indefinite" rotate="auto" /> -->
  </circle>
</svg>
        动画属性
动画目标元素属性
href
href属性用于指定动画的目标对象。它可以是一个链接,也可以是一个ID引用。用作链接时,动画将应用于链接指向的元素。用作ID引用时,动画将应用于具有相应ID的元素。通常情况下是将动画元素写在某个元素当中,这样目标元素将是当前动画元素的直接父元素。
MDN:URL必须只指向一个能够成为给定动画元素目标的目标元素。如果URL指向多个目标元素,如果给定的目标元素不能成为给定动画元素的目标,或者如果给定的目标元素不是当前文档的一部分,则动画元素不会影响任何目标元素。但是,动画元素在其计时属性方面仍将正常运行。
动画属性目标属性
attributeName
attributeName属性用于指定动画作用的CSS/SVG属性名称。在上面的几个栗子我们已经认识它了。
注意:有些属性不是可动画属性,比如CSS
display、SVGfill等属性,详细的CSS动画属性可以查看CSS动画相关属性,而SVG属性是否可动画可以在MDN查找相应的属性。
动画时间属性
begin和end
begin属性用于指定动画的开始时间。
end属性定义一个动画的结束值,可以结束持续时间,当动画结束之后,元素恢复到原来的状态。
取值
begin属性接受一个以分号分隔的值列表(<begin-value-list>),其中每个值可以是以下值之一:
- 
<offset-value>:定义一个时钟值,表示相对于SVG文档开始的时间点(通常是load或DOMContentLoaded事件),示例如下:
可以是负值,动画将会立即开始,不会等待指定的延迟时间,并且是从正值的位置开始播放动画。比如设置动画持续时间为
3s,begin为-2s,动画会从正值2的位置开始立刻播放。 - 
<syncbase-value>:定义syncbase和该syncbase的可选偏移量。即元素的动画开始时间是相对于另一个动画的开始或活动结束时间定义的。格式为:id.[begin | end],其中id是另一个动画对象的id。 - 
<repeat-value>:定义重复事件。格式为:id.repeat(number)。 - 
<event-value>:在特定事件发生时开始动画,事件包括鼠标、键盘、加载等事件。 - 
<accessKey-value>:定义触发动画的访问键,按下访问键播放动画。访问键通过accessKey(键名)方法指定。 - 
<wallclock-sync-value>:将动画开始时间定义为实际时钟时间。通过wallclock(time)指定,其中time的格式基于 ISO 8601 中定义的语法。 - 
indefinite:动画的开始将由beginElement()方法调用或指向该元素的超链接决定。 
当值为<syncbase-value>、<event-value>、<repeat-value>或<accessKey-value>可以附加<offset-value>中定义的可选偏移值。🌰:
            
            
              html
              
              
            
          
          <svg 
  width="500" 
  height="500"
  xmlns="http://www.w3.org/2000/svg">
  <rect width="100" height="100" fill="lightblue" id="rect1">
    <animate 
      attributeName="width"
      from="100" to="500"
      dur="10s"
      begin="click; 2s"
      repeatCount="3" />
  </rect>
</svg>
        当点击元素之后,动画开始播放,当动画播放到2s时,动画回到原点重新播放。不影响repeatCount="3"。
dur
dur属性用于指定动画的持续时间。
注意:如果动画没有指定
dur属性,则动画是无限期,动画不会播放。
取值
| 值类型/关键字 | 简介 | 
|---|---|
<clock-value> | 
指定简单持续时间的长度。格式为hh:mm:ss.iii或hh:mm:ss.iii等。 | 
media | 
表示动画的持续时间与媒体元素的持续时间相匹配。媒体元素可以是音频或视频文件,通过设置dur="media",可以使动画的持续时间与媒体元素的播放时间保持同步。 | 
indefinite | 
将持续时间指定为无限期。 | 
min和max
min和max属性分别指定活动动画持续时间的最小值和最大值。当动画的持续时间小于min值时,动画将被强制延长到min值。反之会被缩短到max值。
这两个属性只是一个指导值,它并不会改变动画的实际持续时间。如果动画的实际持续时间大于min值/小于max值,动画将按照实际持续时间播放。只有当动画的实际持续时间小于min值/大于max值时,动画才会被延长到min值/缩短到max的长度。
这两个值可以用于当dur="media",动画的持续时间与媒体元素的播放时间保持同步时,约束动画的持续时间。
restart
restart属性指定动画是否可以重新启动。可以实现在播放动画期间,触发某些操作时使得动画重新开始的效果。
取值
| 值类型/关键字 | 简介 | 
|---|---|
always | 
可以随时重新启动动画。 | 
media | 
表示动画只有在不活动时(即在活动结束之后)才能重新启动。在动画活动期间重新启动动画的尝试将被忽略。 | 
indefinite | 
无法重新启动动画。 | 
repeatCount和repeatDur
repeatCount和repeatDur分别指定表示动画将发生的次数和重复动画的总持续时间。
取值
| 值类型/关键字 | 简介 | 
|---|---|
number(repeatCount的属性) | 
指定迭代次数。值可以是分数或负数,当值为分数时,仅表示整个动画持续时间的一部分 | 
<clock-value>(repeatDur的属性) | 
指定重复动画的持续时间。 | 
indefinite | 
该值表示动画将无限期地重复。 | 
fill
fill指定动画的填充模式。即当动画结束之后是否还需要保持动画效果。
取值
| 值类型/关键字 | 简介 | 
|---|---|
remove | 
动画的持续时间结束后,动画效果会移除(不再应用)。 | 
freeze | 
动画的持续时间结束后,动画效果保持在结束时的状态。 | 
动画取值属性
from、to和by
from属性定义动画的起始值,指定了动画元素的初始状态;to属性指定了动画的结束值;而by属性指定在动画过程中修改的属性的相对偏移值。
当我们需要对一个动画进行从一个状态变化到另一个状态时,使用from和to属性;而需要对一个动画进行从当前值逐渐增加一个固定的值时,使用from和by属性。
这三个属性的具体值类型根据attributeName而定。如果指定了attributeName对应的属性,则可以不指定from属性。
🌰:
            
            
              html
              
              
            
          
          <svg 
  width="300" 
  height="300"
  xmlns="http://www.w3.org/2000/svg"
  >
  <!-- 宽度从100变化到150 -->
  <rect width="100" height="100" fill="lightblue">
    <animate attributeName="width" fill="freeze" to="150" dur="3s" />
  </rect>
  <!-- 将宽度100增加50 -->
  <rect y="110" width="100" height="100" fill="lightpink">
    <animate attributeName="width" fill="freeze" by="50" dur="3s" />
  </rect>
</svg>
        value
value属性定义动画过程中values序列的值列表。
注意:如果指定了
values属性,则会忽略from、to和by属性。
取值
values属性可以接受一个以分号分隔 的值列表。每个值都表示动画在不同时间点的关键帧值。具体的值类型由href和attributeName属性决定。
🌰:
            
            
              html
              
              
            
          
          <svg 
  width="300" 
  height="300"
  xmlns="http://www.w3.org/2000/svg"
  >
  <!-- 矩形的颜色变化过程lightblue → lightpink → lightgreen -->
  <rect width="100" height="100" fill="lightblue" >
    <animate attributeName="fill" dur="5s" values="lightblue;lightpink; lightgreen" />
  </rect>
  <!-- 矩形的宽度变化过程200; 150; 300 -->
  <rect y="120" width="100" height="100" fill="purple" >
    <animate attributeName="width" dur="3s" values="200; 150; 300" />
  </rect>
</svg>
        calcMode
calcMode属性用于指定动画的插值模式。
插值是基于已知值计算值的过程。在动画中,插值用于计算动画在关键帧之间的过渡值。通过插值,可以实现平滑的动画效果,使得动画在关键帧之间的过渡更加自然和流畅。
取值
| 值类型/关键字 | 简介 | 
|---|---|
discrete | 
指定动画函数将从一个值跳转到下一个值,而无需任何插值。 | 
linear | 
动画的值会在关键帧之间线性插值。动画的值会在关键帧之间平滑过渡,呈现出线性的变化。 | 
paced | 
动画的值会根据关键帧之间的时间间隔进行插值。动画的值会根据关键帧之间的时间间隔进行调整,以保持动画的节奏感。 | 
spline | 
动画的值会根据关键帧之间的贝塞尔曲线进行插值。动画的值会根据关键帧之间的贝塞尔曲线进行平滑过渡,呈现出曲线的变化。 | 
keyTimes和keySplines
keyTimes属性指定动画节奏的时间值列表。可以用来控制动画中关键帧的时间间隔和持续时间。
keySplines属性定义一组与keyTimes列表关联的贝塞尔曲线控制点,定义控制间隔步调的三次贝塞尔函数。
注意:只有当
calcMode="spline"时,keySplines属性才会生效,否则会被忽略。
取值
- 
keyTimes属性的值是以分号分隔的值列表。具体取决于calcMode:
 另外,列表项的个数要与`values`属性相同: ```html ``` 注意:- 每个列表项的值必须大于前一个列表项的值。
 - 如果没有指定
dur属性,keyTimes会被忽略。 
 - 
keyTimes属性的值是以分号分隔的控制点描述列表。列表项的总数是keyTimes属性值列表的总数 - 1 。每个控制点的格式为:x1 y1 x2 y2(值范围均为0~1)。 
🌰:
            
            
              html
              
              
            
          
          <svg 
  width="300" 
  height="300"
  xmlns="http://www.w3.org/2000/svg"
  >
  <rect width="100" height="100" fill="lightblue">
    <animate 
      attributeName="x" 
      values="10; 50; 100; 150; 200"
      dur="2s" 
      repeatCount="indefinite" />
  </rect>
  <rect y="110" width="100" height="100" fill="lightpink">
    <animate 
      attributeName="x" 
      values="10; 100; 200"
      dur="2s"
      calcMode="spline"
      repeatCount="indefinite"
      keyTimes="0; 0.5; 1"
      keySplines="0.68 0.55 0.27 0.55; 0.68 0.55 0.27 0.55" />
  </rect>
</svg>
        动画附加属性
additive和accumulate
additive和accumulate属性分别控制动画是否可累加和动画是否为累加动画。
累加是指在动画播放过程中,每次播放完毕后,目标元素的属性值将会累加上一次动画的结果。
取值
| 值类型/关键字 | 简介 | 
|---|---|
sum | 
对additive:指定动画将添加到属性和其他较低优先级动画的基础值中。 对accumulate:指定第一次迭代之后的每个重复迭代都基于上一次迭代的最后一个值构建加上初始值。 | 
replace(additive的属性,默认值) | 
指定动画将覆盖属性和其他较低优先级动画的基础值,动画的效果将会替换属性的当前值,而不是叠加到它上面。会受到by和to的影响。 | 
none(accumulate的属性) | 
指定重复迭代不是累加的。 | 
注意:
- 需要明确指定初始值和结束值。
 - 如果动画不是重复的,则会忽略这两个属性。
 
🌰:
            
            
              html
              
              
            
          
          <svg 
  width="300" 
  height="300"
  xmlns="http://www.w3.org/2000/svg"
  >
  <rect width="50" height="50" fill="blue">
    <animate attributeName="width" from="50" to="100" dur="2s" repeatCount="3" accumulate="sum"  />
    <animate attributeName="height" from="50" to="100" dur="2s" repeatCount="3" accumulate="sum" />
  </rect>
</svg>
        动画网站/库
一个SVG文件通常是很复杂的,在网上下载一个SVG文件,比如在iconfont中下载一个这样的SVG文件:

它的实现代码是这样的:
            
            
              html
              
              
            
          
          <svg t="1688124965784" class="icon" style="width: 1em;height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3562">
  <path d="M595.72419 50.029714l0.219429 0.24381 273.310
  476 284.476952A292.571429 292.571429 0 0 1 950.857143 537.42
  9333v320a146.285714 146.285714 0 0 1-146.285714 146.285715H219.428571a146.28
  5714 146.285714 0 0 1-146.285714-146.285715V543.744a292.571429 292.571429 0 
  0 1 89.209905-210.334476L457.825524 47.713524a97.52381 97.52381 0 0 1 
  137.898666 2.31619zM512 491.715048a146.285714 146.285714 0 1 0 0 29
  2.571428 146.285714 146.285714 0 0 0 0-292.571428z" 
  fill="#FD4953" 
  p-id="3563" 
  data-spm-anchor-id="a313x.7781069.0.i0" 
  class="selected"></path>
</svg>
        虽然内部只有一个<path>,但是这个<path>还是比较复杂的。一个静态的SVG文件都这么复杂,那一个具有精美动画的SVG文件的复杂的可想而知,因此日常如果要制作一个动态的SVG时,肯定需要用到各种工具网站/库,当然,也可以自己手写,其实如果是用工具库来写SVG动画,其原理跟我们平时的CSS动画和Javascript动画差不多。
接下来我们就来看看一些SVG动画工具库和网站。
工具库
用工具库制作SVG动画的方式基本都是通过CSS或Javascript的方式来访问SVG元素,这里以Animate.css和GSAP为例演示用CSS和Javascript制作SVG动画。
注意:
- 下面我是将
<script>提到了外面,但实际上像<style>和<script>等元素可以直接写在SVG中,只不过稍微有点不同,这点下篇文章再说明,这里先分开写。 - 在SVG中不能直接使用
<link>和<script src='...'>来引入第三方CSS和JS库,有几种方式:- 在同一文件的SVG外部
<link>和<script>来引入第三方CSS和JS库。 - 通过
<foreignObject>(下篇文章会介绍)嵌入<link>和<script>,并正常使用引入第三方CSS和JS库。 - JS可以使用ES Module在
<script>中通过import语法来引入第三方JS库。 
 - 在同一文件的SVG外部
 
CSS方式:
            
            
              html
              
              
            
          
          <svg  
  width="120"
  height="120"
  xmlns="http://www.w3.org/2000/svg">
  <foreignObject>
    <link
  rel="stylesheet"  
  href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
  />
  </foreignObject>
  <!-- rubberBand -->
  <rect 
    height="100" 
    width="100" 
    fill="lightblue" 
    class="animate__animated animate__rubberBand animate__infinite">
  </rect>
</svg>
        Javascript方式如下,与CSS方式不同,可以在<script>标签中使用href属性来引入第三方JS库:
            
            
              html
              
              
            
          
          <svg  
  width="300"
  height="300"
  xmlns="http://www.w3.org/2000/svg">
  <rect 
    height="100" 
    width="100" 
    fill="lightblue" 
    class="rect-animate">
  </rect>
  <script type="module">
    import animejs from 'https://cdn.jsdelivr.net/npm/animejs@3.2.1/+esm'
    animejs({
      targets: '.rect-animate',
      translateX: 200,
      loop: true,
      direction: 'alternate',
      easing: 'easeInOutExpo'
    });
  </script>
</svg>
        anime.js:与任何其他DOM属性一样,所有包含至少一个数值的SVG属性都可以进行动画处理。
GSAP
GSAP(GreenSock Animation Platform),是一个用于创建高性能动画的JavaScript库。它提供了丰富的动画功能和灵活的控制选项,使开发人员能够轻松地创建各种复杂的动画效果。
这里特别介绍它的TimelineLite和TimelineMax类:
- 
TimelineLite是一个轻量级的时间轴类,它允许我们按顺序添加和控制动画序列。可以使用它来创建简单的时间轴动画.时间轴动画是一种通过在时间轴上按顺序添加和控制动画序列来创建动画效果的技术。它允许我们在不同的时间点上定义不同的动画效果,并控制它们的播放顺序、持续时间和其他属性。
时间轴动画的工作原理是通过在每个时间点上更新动画效果的属性值来实现的。当时间轴播放时,它会根据每个动画效果的持续时间和缓动函数来计算当前时间点的属性值,并将其应用于相应的元素或对象上。
 - 
TimelineMax是TimelineLite的扩展,它提供了更多的功能和控制选项。除了TimelineLite的所有功能外,TimelineMax还具有一些额外的方法和属性,例如repeat()和yoyo(),用于重复播放和反向播放动画。它还支持更复杂的时间轴控制,如添加标签、控制动画的播放速度和暂停/恢复动画等。 
🌰:
            
            
              html
              
              
            
          
          <svg  
  width="300"
  height="300"
  xmlns="http://www.w3.org/2000/svg">
  <rect 
    height="100" 
    width="100" 
    fill="lightblue" 
    class="rect-animate">
  </rect>
  <script type="module">
    import {TimelineLite} from 'https://cdn.jsdelivr.net/npm/gsap@3.12.2/+esm'
    const rect = document.querySelector('.rect-animate')
    // 创建TimelineLite实例
    const tl = new TimelineLite();
    // 添加动画效果
    tl.to(rect, 1, {x: 100})
    .to(rect, 1, {y: 100})
    .to(rect, 1, {x: 0})
    .to(rect, 1, {y: 0});
    // 播放动画
    tl.play();
  </script>
</svg>
        网站
Svgator(部分收费)
Svgator是一个在线动画制作工具,它允许用户创建和编辑矢量动画。用户可以使用Svgator的直观界面来设计和调整动画,包括路径动画、形状变换、颜色渐变等。Svgator还提供了丰富的动画效果和过渡选项,使用户能够创建出令人惊叹的动画效果。此外,Svgator还支持导出为SVG、GIF和视频等类型的文件,方便用户在网页、应用程序和其他平台上使用动画。

SVG Circus(免费)
SVG Circus也是一个在线工具,用于创建和定制可缩放矢量图形(SVG)动画。它提供了一个简单易用的界面,让用户可以通过拖放和调整参数来创建各种各样的动画效果。相比Svgator,SVG Circus的界面更简洁更容易理解,而且它是免费的,不过他只能导出SVG格式的文件。

。。。
上面那些网站制作动画的方式也分为CSS和Javascript方式(后者更多一点),结果跟用工具库差不多。另外,下载下来的SVG动画文件中,通常都包含一个一大段内容并且压缩过的<style>和<script>。那我们自己用工具库的方式来制作时如何封装呢,这里主要讨论用Javascript制作动画的方式。
第一种方式是用原生CSS和Javascript手写,简单粗暴,不过这种方式通常用于简单的动画效果,而且我们不一定保证在所有环境中都能生效。
第二种方式就是上面的JS方式,写好之后压缩一下就好了,不过这种方式不能保证CDN的链接是可用的,万一CDN服务商崩了呢。
第三种方式是通过构建工具 + NPM第三方库来制作SVG动画,就是搭建的时候费点劲。
我个人更喜欢先自己写好静态结构,再上传到动画制作网站中制作动画,我个人觉得用那些网站画一个静态结构没那么自由。