SVG 动画指南 (SMIL)

概述

SVG 图形可以使用animation elements(动画元素),进行动画处理。动画元素最初是在SMIL动画规范中定义的;这些element包括:

  • <animate></animate>– 它允许你在一段时间内为标记的属性和特性设置动画。
  • <set></set>– 这是 animate 的一种方便的简写形式,可用于将动画值分配给非数字属性和属性,例如可见性属性。
  • <animatemotion></animatemotion>– 沿运动路径移动元素。
  • <animatecolor></animatecolor>– 随着时间的推移修改特定属性或特性的颜色值。请注意,该元素已被弃用,取而代之的是简单地使用 animate 元素来定位可以采用颜色值的属性。尽管它仍然存在于 SVG 1.1 规范中,但明确指出它已被弃用;并且它已从 SVG 2 规范中完全删除。

除了 SMIL 规范中定义的动画元素之外,SVG 还包括与 SMIL 动画规范兼容的扩展;这些扩展包括扩展元素功能的属性和附加动画元素。SVG 扩展包括:

  • <animatetransform></animatetransform>– 允许你随着时间的推移为 SVG 的转换属性之一设置动画,例如transform属性。
  • path  (attribute)  – 允许在animateMotion元素的路径属性中指定 SVG 路径数据语法的任何特征(SMIL 动画只允许路径属性中的 SVG 路径数据语法的子集)。animateMotion我们将在下一节中详细讨论。
  • <mpath></mpath>– 与animateMotion元素结合使用以引用将用作运动路径的运动路径。mpath 元素包含在animateMotion元素内部,位于结束标记之前。
  • keypoints  (attribute)  – 用作属性animateMotion以提供对运动路径动画速度的精确控制。
  • rotate  (attribute)  – 用作animateMotion控制对象是否自动旋转的属性,使其 x 轴指向与运动路径的方向切向量相同(或相反)的方向。此属性是使运动沿路径按预期工作的关键。有关更多信息,请参见animateMotion部分。

SVG 动画在本质上可以类似于 CSS 动画和过渡。创建关键帧、移动物体、改变颜色等。但是,它们可以做一些 CSS 动画做不到的事情,我们将对此进行介绍。

为什么要使用 SVG 动画?

SVG 可以使用 CSS(幻灯片)设置样式和动画。基本上,任何可以应用于 HTML 元素的转换或过渡动画也可以应用于 SVG 元素。但是有些 SVG 属性不能通过 CSS 进行动画处理,但可以通过 SVG 进行动画处理。例如,SVG 路径带有一组定义该路径形状的数据(属性)。d=""这些数据可以通过 SMIL 进行修改和动画化,但不能通过 CSS。这是因为 SVG 元素是由一组称为 SVG表示属性的属性描述的。其中一些属性可以使用 CSS 设置、修改和设置动画,而其他属性则不能。

所以,很多动画和效果在这个时候根本无法用CSS来实现。可以使用 JavaScript 或从 SMIL 派生的声明性 SVG 动画来填补 CSS SVG 动画空白。

如果你更喜欢使用 JavaScript,我推荐使用Dmitry Baranovskysnap.svg,它被描述为“SVG 中的 jQuery”。这是一个例子的集合

或者,如果你更喜欢更具声明性的动画方法,则可以使用我们将在本指南中介绍的 SVG 动画元素!

imgSMIL 相对于 JS 动画的另一个优势是,当 SVG 作为 CSS 嵌入或用作CSS时,JS 动画不起作用background-image。SMIL 动画在这两种情况下都有效(或者应该,浏览器支持待定)。在我看来,这是一个很大的优势。因此,你可能会发现自己选择 SMIL 而不是其他选项。本文是帮助你立即开始使用 SMIL 的指南。

浏览器支持和回退

浏览器对 SMIL 动画的支持相当不错。它们适用于除 Internet Explorer 和 Opera Mini 之外的所有浏览器。有关浏览器支持的全面概述,你可以参考我可以使用上的兼容性表

如果你需要为 SMIL 动画提供回退,你可以使用Modernizr即时测试浏览器支持。如果不支持 SMIL,你可以提供某种回退(JavaScript 动画、替代体验等)。

指定动画的目标xlink:href

无论选择四种动画元素中的哪一种,都需要指定该元素定义的动画目标。

为了指定目标,你可以使用该xlink:href属性。该属性采用对元素的 URI 引用,该元素是此动画的目标,因此会随着时间的推移而修改。目标元素必须是当前 SVG 文档片段的一部分。

<rect id="cool_shape" ...="">
  <animate xlink:href="#cool_shape" ...=""></animate>
</rect>

如果你以前遇到过 SVG 动画元素,你可能已经看到它们嵌套在它们应该设置动画的元素内。这是可能的,也符合规范:

如果xlink:href未提供该属性,则目标元素将是当前动画元素的直接父元素。

<rect id="cool_shape" ...="">
  <animate ...=""></animate>
</rect>

所以如果你想将动画“封装”到它所应用的元素中,你可以这样做。如果你想让动画在文档的其他地方分开,你也可以这样做,并使用指定每个动画的目标xlink:href。这两种方式都很好。

attributeName使用和指定动画的目标属性attributeType

所有动画元素还共享另一个属性:attributeName. 该attributeName属性用于指定你正在设置动画的属性的名称。

例如,如果你想为 x 轴上 a 的中心位置设置动画,你可以通过指定as属性cx的值来实现。attributeName

attributeName只接受一个值,而不是值列表,因此,你一次只能为一个属性设置动画。如果要为多个属性设置动画,则需要为元素定义多个动画。这是我希望有所不同的东西,而且我认为 CSS 比 SMIL 有优势。但话又说回来,由于其他动画属性的值(我们将在接下来介绍),一次只定义一个属性名称才有意义,否则其他属性值可能会变得太复杂而无法使用。

在指定属性名称时,可以添加 XMLNS(XML 命名空间的缩写)前缀来指示属性的命名空间。命名空间也可以使用attributeType属性指定。例如,一些属性是 CSS 命名空间的一部分(这意味着该属性也可以作为 CSS 属性找到),而其他属性则仅限于 XML。可以在此处找到显示这些属性的表格。表格中的属性并不是所有的 SVG 属性。它们只是可以使用 CSS 设置的。其中一些已经作为 CSS 属性提供。

如果 的值attributeType未明确设置或设置为auto,则浏览器必须首先在 CSS 属性列表中搜索匹配的属性名称,如果未找到,则在默认 XML 命名空间中搜索该元素。

例如,以下代码片段opacity为 SVG 矩形设置了动画。由于该opacity属性也可用作 CSS 属性,因此attributeType设置为 CSS 命名空间:

<rect>
  <animate
    attributetype="CSS"
    attributename="opacity" 
    from="1" 
    to="0" 
    dur="5s" 
    repeatcount="indefinite">
  </animate>
</rect>

我们将在下面的示例中介绍其他动画属性。除非另有说明,否则所有动画属性对于所有动画元素都是通用的。

在一段时间内将元素的属性从一个值动画化为另一个值,并指定结束状态:frombytodur``fill

让我们从将一个圆圈从一个位置移动到另一个位置开始。我们将通过更改其cx属性值(指定其中心的 x 位置)来实现。

我们将使用元素来做到这一点。此元素用于一次为一个属性设置动画。采用数值和颜色的属性通常使用 . 有关可以设置动画的属性列表,请参阅此表

为了在一段时间内将一个值更改为另一个值,from使用to、 和dur属性。除了这些之外,你还需要指定动画何时开始使用该begin属性。

<circle id="my-circle" r="30" cx="50" cy="50" fill="orange">
  <animate xlink:href="#my-circle" attributename="cx" from="50" to="450" dur="1s" begin="click" fill="freeze"></animate>
</circle>

在上面的例子中,我们定义了一个圆,然后在这个圆上调用了一个动画。圆心从初始位置沿 x 轴移动 50 个单位到 450 个单位。

begin值设置为click。这意味着圆圈将在单击时移动。你也可以将此值设置为时间值。例如,begin="0s"将在页面加载后立即启动动画。你可以通过设置正时间值来延迟动画。 例如,begin="2s"加载后两秒开始播放动画。

更有趣的begin是,你可以定义值,例如在单击元素后一秒开始播放click + 1s动画!此外,你可以使用其他值来同步动画,而无需计算其他动画的持续时间和延迟。稍后会详细介绍。****

dur属性类似于animation-durationCSS 中的等价物。

fromandto属性类似于CSS中动画块中的fromand关键帧:to``@keyframe

@keyframes moveCircle {
  from { /* start value */ }
  to { /* end value */ }
}

属性(不幸的是与定义元素填充颜色fill的属性同名fill)类似于animation-fill-mode属性,它指定元素是否应在动画结束后返回到其初始状态。SVG 中的值与 CSS 中的值类似,只是使用不同的名称:

  • freeze:动画效果定义为将效果值冻结在活动持续时间的最后一个值。动画效果在文档持续时间的剩余时间内“冻结”(或直到动画重新启动)。
  • remove:当动画的活动持续时间结束时,动画效果将被移除(不再应用)。动画活动结束后,动画不再影响目标(除非重新启动动画)。

尝试更改现场演示中的值以查看动画如何受到影响:

https://codepen.io/SaraSoueidan/pen/QWYWyr/e883265849147a0a4b712c5960c448a8

by属性用于指定动画的相对偏移量。顾名思义,你可以使用它来指定你希望动画进行的数量。的效果by几乎只有在动画持续时间以离散步骤前进时才可见,类似于它与 CSSsteps()函数一起使用的方式。相当于 CSSsteps()功能的 SVG 是calcMode="discrete". 我们将calcMode在本文后面介绍该属性。

另一种效果by更明显的情况是当你只指定to属性时。一个例子是,如果你将它与set我们也将在本文后面介绍的元素一起使用。

最后但并非最不重要的by一点是,在处理叠加和累积动画时也很有用。我们将在本文后面讨论。

重启动画restart

防止动画在活动时重新启动可能很有用。为此,SVG 提供了restart属性。你可以将此属性设置为三个可能值之一:

  • always:动画可以随时重新开始。这是默认值。
  • whenNotActive: 动画只有在不活动时才能重新开始(即活动结束后)。在活动期间尝试重新启动动画将被忽略。
  • never:元素不能在父时间容器的当前简单持续时间的剩余时间内重新启动。(在 SVG 的情况下,由于父时间容器是 SVG 文档片段,因此在文档持续时间的剩余时间内无法重新启动动画。)

命名动画并同步它们

假设我们要为圆圈的位置颜色设置动画,以便在移动动画结束时发生颜色变化。我们可以通过将begin变色动画的值设置为等于dur移动动画的值来做到这一点;这就是我们通常在 CSS 中的做法。

然而,SMIL 有一个很好的事件处理特性。我们之前提到过,该begin属性接受像click + 5s. 该值称为“事件值”,在这种情况下由事件引用和后跟“时钟值”组成。这里有趣的部分是第二部分的命名:“时钟值”。为什么它不只是一个“时间价值”呢?答案是你可以直接使用时钟值,如“10min”或“01:33”,相当于“1 分 33 秒”,甚至“02:30:03”(两小时 30 分钟,和 3 秒)。在撰写本文时,时钟值尚未在任何浏览器中完全实现。

因此,如果我们回到之前的演示并使用click + 01:30,如果浏览器开始支持它,则动画将在单击圆圈后 1 分 30 秒触发。

它可以接受的另一种值是另一个动画的 ID,后跟一个事件引用。如果你有两个(或更多)动画(无论它们是否应用于同一个元素!)并且你想要同步它们以便其中一个相对于另一个开始,你可以这样做而不必知道持续时间另一个动画。

例如,在下一个演示中,蓝色矩形在圆形动画开始后 1 秒开始移动。这是通过为每个动画提供一个ID,然后将该 ID 与begin事件一起使用来完成的,如以下代码所示:

<circle id="orange-circle" r="30" cx="50" cy="50" fill="orange">
  <rect id="blue-rectangle" width="50" height="50" x="25" y="200" fill="#0099cc"></rect>
  <animate 
    xlink:href="#orange-circle" 
    attributename="cx" 
    from="50" 
    to="450" 
    dur="5s" 
    begin="click" 
    fill="freeze" 
    id="circ-anim">
  </animate>
  <animate 
    xlink:href="#blue-rectangle" 
    attributename="x" 
    from="50" 
    to="425" 
    dur="5s" 
    begin="circ-anim.begin + 1s" 
    fill="freeze" 
    id="rect-anim">
  </animate>
</circle>

begin="circ-anim.begin + 1s"是告诉浏览器在圆形开始后 1 秒开始矩形动画的部分。你可以查看现场演示:

https://codepen.io/SaraSoueidan/pen/NWoWZo/55195eee8647f438525b852000504c7a

你还可以在圆形动画结束后使用end事件启动矩形动画:

<animate 
  xlink:href="#blue-rectangle" 
  attributename="x" 
  from="50" 
  to="425" 
  dur="5s"
  begin="circ-anim.end" 
  fill="freeze" 
  id="rect-anim">
</animate>
 

你甚至可以在圆圈动画结束之前启动它:

<animate
  xlink:href="#blue-rectangle"
  attributename="x" 
  from="50" 
  to="425" 
  dur="5s" 
  begin="circ-anim.end - 3s" 
  fill="freeze" 
  id="rect-anim">
</animate>
 

重复动画repeatCount

如果你想多次运行一个动画,你可以使用repeatCount属性来做到这一点。你可以指定希望它重复的次数,或者使用indefinite关键字让它无限重复。因此,如果我们将圆圈的动画重复两次,代码将如下所示:

<animate
  xlink:href="#orange-circle"
  attributename="cx" 
  from="50" 
  to="450" 
  dur="5s" 
  begin="click" 
  repeatcount="2"
  fill="freeze" 
  id="circ-anim">
</animate>
 

你可以在此处查看现场演示。在演示中,我将重复计数设置为2在圆圈和indefinite正方形上。

请注意动画是如何从初始from值而不是它在动画结束时达到的值重新开始的。不幸的是,SMIL 不包括像 CSS 动画允许我们做的那样在开始值和结束值之间来回切换的方法。在 CSS 中,该animation-direction属性指定动画是否应该在某些或所有循环或迭代中反向播放。animation-direction: alternatevalue表示奇数次的动画循环迭代按正常方向播放,偶数次的动画循环迭代按反方向播放。这意味着第一个循环将从头播放到结尾,然后第二个循环将从结尾回到开头播放,然后第三个循环将从头播放到结尾,依此类推。

在 SMIL 中,要做到这一点,你必须使用 JavaScript 显式更改fromto属性的值。Big Bite Creative 的乔恩·麦克帕特兰 (Jon McPartland) 不久前写了一篇文章,解释了他是如何为自己制作的菜单图标动画做到这一点的。

另一种解决方法是将结束值指定为中间值,然后让结束值与初始值相同。例如,你可以将动画设置为以from一个值开始,并以与 相同的值结束to,只是你将要设置为最终值的值指定为 和 之间的中间fromto
在 CSS 中,我们会使用这样的东西来做到这一点:

@keyframes example {
  from, to {
    left: 0;
  }

  50% {
    left: 300px;
  }
}

SMIL 中的等价物是使用values属性,我们将在稍后解释。

也就是说,上述解决方法可能对你有用,也可能不适合你,具体取决于你所追求的动画类型,以及你是否正在链接动画、重复动画或添加动画。

这是 Miles Elam 使用一些延迟开始时间制作的漂亮、简单的无限动画:

https://codepen.io/mileselam/pen/QWVYXR

限制重复时间repeatDur

如果动画持续很长时间,将元素设置为无限重复可能会令人讨厌或对用户不友好。因此,将重复时间限制在某个时间段内,并在相对于文档开头的某个时间后停止重复可能是个好主意。这称为演示时间

呈现时间指示时间线中相对于给定文档片段的文档开始的位置。它是使用repeatDur属性指定的。它的语法类似于时钟值的语法,但它不是相对于另一个动画事件或交互事件,而是相对于文档的开头。

例如,以下代码片段将在文档开始后 1 分 30 秒停止动画的重复:

<animate 
  xlink:href="#orange-circle" 
  attributename="cx" 
  from="50" 
  to="450" 
  dur="2s" 
  begin="0s" 
  repeatcount="indefinite" 
  repeatdur="01:30" 
  fill="freeze" 
  id="circ-anim">
</animate>
 

这是现场演示:

根据重复次数同步动画

现在让我们回到两个动画主题之间的同步。实际上,在 SMIL 中,你可以同步动画,以便一个动画根据另一个动画的重复次数启动。例如,你可以在另一个动画的第 n 次重复之后开始动画,加上或减去你可能想要添加的时间量。
以下示例在圆形动画的第二次重复时启动矩形动画:

<animate 
  xlink:href="#blue-rectangle"
  attributename="x" 
  from="50" 
  to="425" 
  dur="5s" 
  begin="circ-anim.repeat(2)" 
  fill="freeze" 
  id="rect-anim">
</animate>
 

以下是一个现场演示,其中矩形动画在第二次重复圆形动画后 2 秒开始播放。

https://codepen.io/SaraSoueidan/pen/rNPMJj/366b9fba478e7ac1de2188f5a2594c3c

是 David Eisenberg 为SVG Essentials(第 2 版) 整理的示例。

控制动画关键帧值:keyTimesvalues

在 CSS 中,我们可以指定我们希望动画属性在动画期间的特定帧中采用的值。例如,如果你正在为元素的左偏移设置动画,而不是直接从 0 到 300 设置动画,你可以为其设置动画,使其在特定帧期间采用特定值,如下所示:

@keyframes example {
  0% {
    left: 0;
  }
  50% {
    left: 320px;
  }
  80% {
    left: 270px;
  }
  100% {
    left: 300px;
  }
}

0%、和是动画的帧20%,每个帧的块中的值每个帧的值。上面描述的效果是一个元素从墙上弹起,然后回到最终位置。80%``100%**

在 SMIL 中,你可以用类似的方式控制每帧的值,但语法完全不同。

要指定关键帧,你可以使用该keyTimes属性。然后使用属性为每个帧指定动画属性的值values。SMIL 中的命名约定非常方便。

如果我们回到我们的移动圆圈,并使用类似于上面 CSS 关键帧中的值,代码将如下所示:

<animate
  xlink:href="#orange-circle" 
  attributename="cx" 
  from="50" 
  to="450" 
  dur="2s" 
  begin="click" 
  values="50; 490; 350; 450" 
  keytimes="0; 0.5; 0.8; 1" 
  fill="freeze" 
  id="circ-anim">
</animate>
 

 

那么我们在那里做了什么?

这里首先要注意的是关键帧时间和中间值被指定为列表。该keyTimes属性是一个以分号分隔的时间值列表,用于控制动画的节奏。列表中的每个时间对应values属性列表中的一个值,并定义了该值何时在动画函数中使用。列表中的每个时间值keyTimes都指定为 0 到 1(含)之间的浮点值,表示动画元素的简单持续时间的比例偏移。所以关键时间类似于 CSS 中的关键时间,不同之处在于,不是将它们指定为百分比,而是将它们指定为分数。

以下是上述代码的现场演示。单击圆圈以启动动画。

请注意,如果使用值列表,则动画将在整个动画过程中按顺序应用这些值。如果指定了列表,则忽略values任何和属性值。from``to``by

在这一点上,还值得一提的是,你可以values在没有属性keyTimes的情况下使用属性——值会自动均匀地分布在整个时间(对于calcModepaced(见下一节)之外的每个值)。

使用自定义缓动控制动画节奏:calcModekeySplines

我将再次进行 CSS-SMIL 比较,因为如果你已经熟悉 CSS 动画,SMIL 语法和概念将更容易理解。

在 CSS 中,你可以选择更改默认的统一动画速度,并使用animation-timing-function属性指定控制动画的自定义缓动函数。计时函数可以是几个预定义关键字之一,也可以是三次贝塞尔函数。后者可以使用工具创建,例如Lea Verou工具。

在 SMIL 中,动画速度是使用calcMode属性指定的。默认动画速度适用linear于所有动画元素,除了animateMotion(我们将在本文后面介绍)。除了linear值之外,你还可以将值设置为:discretepacedspline

  • discrete指定动画函数将在没有任何插值的情况下从一个值跳到下一个值。这类似于steps()CSS 中的函数。
  • paced类似于linear,只是它会忽略由 定义的任何中间进度时间keyTimes。它计算出后续值之间的距离并相应地划分时间。如果你的值全部按线性顺序排列,你将不会注意到差异。但如果它们来回移动,或者如果它们是颜色(被视为三维矢量值),你肯定会看到中间值。这是 Amelia Bellamy-Royds 提供的演示,calcMode它显示了到目前为止提到的三个值之间的差异。
  • 接受的第四个值calcModespline。它values根据三次贝塞尔样条定义的时间函数从列表中的一个值插值到下一个值。样条的点在keyTimes属性中定义,每个区间的控制点在keySplines属性中定义。

你可能已经注意到最后一句话中的新属性:keySplines属性。那么,keySplines属性有什么作用呢?

同样,对于 CSS 等价物。

在 CSS 中,你可以每个关键帧内指定动画速度,而不是为整个动画指定一个动画速度。这使你可以更好地控制每个关键帧动画的处理方式。使用此功能的一个示例是创建弹跳球效果。其关键帧可能如下所示:

@keyframes bounce {
  0% {
    top: 0;
    animation-timing-function: ease-in;
  }
  15% {
    top: 200px;
    animation-timing-function: ease-out;
  }
  30% {
    top: 70px;
    animation-timing-function: ease-in;
  }
  45% {
    top: 200px;
    animation-timing-function: ease-out;
  }
  60% {
    top: 120px;
    animation-timing-function: ease-in;
  }
  75% {
    top: 200px;
    animation-timing-function: ease-out;
  }
  90% {
    top: 170px;
    animation-timing-function: ease-in;
  }
  100% {
    top: 200px;
    animation-timing-function: ease-out;
  }
}

我们可以使用相应的 cubic-bezier 函数,而不是关键字缓动函数:

  • ease-in=cubic-bezier(0.47, 0, 0.745, 0.715)
  • ease-out=cubic-bezier(0.39, 0.575, 0.565, 1)

让我们首先为我们的橙色圆圈指定关键时间和**列表values**以进行相同的弹跳效果:

<animate 
  xlink:href="#orange-circle" 
  attributename="cy" 
  from="50" 
  to="250" 
  dur="3s" 
  begin="click" 
  values="50; 250; 120;250; 170; 250; 210; 250" 
  keytimes="0; 0.15; 0.3; 0.45; 0.6; 0.75; 0.9; 1" 
  fill="freeze" 
  id="circ-anim">
</animate>
 

动画将在点击时开始,一旦达到结束值就会冻结。接下来,为了指定每个关键帧的速度,我们将添加keySplines属性。

keySplines属性采用与列表关联的贝塞尔曲线控制点keyTimes集,定义控制间隔步调的三次贝塞尔曲线函数。属性值是以分号分隔的控制点描述列表。每个控制点描述都是一组四个值:x1 y1 x2 y2,描述一个时间段的贝塞尔曲线控制点。这些值必须都在 0 到 1 的范围内,除非将calcMode设置为 ,否则该属性将被忽略spline

不是将三次贝塞尔函数作为值,keySplines而是采用用于绘制曲线的两个控制点的坐标。可以在以下从 Lea 的工具截取的屏幕截图中看到控制点。屏幕截图还显示了每个点的坐标,每个点的颜色都与该点本身的颜色相同。对于keySplines属性,我们将使用这些值来定义关键帧动画的速度。

SMIL 允许这些值用逗号和可选的空格分隔,或者只用空格分隔。定义关联段的keyTimes值是贝塞尔曲线“锚点”,这些keySplines值是控制点。因此,必须有组控制点比有少keyTimes

如果我们回到弹跳球的例子,ease-inease-out函数的控制点坐标如下图所示:

因此,为了将其转换为 SVG 动画元素,我们得到以下代码:

<animate 
  xlink:href="#orange-circle" 
  attributename="cy" 
  from="50" 
  to="250" 
  dur="3s" 
  begin="click" 
  values="50; 250; 120;250; 170; 250; 210; 250" 
  keytimes="0; 0.15; 0.3; 0.45; 0.6; 0.75; 0.9; 1" 
  keysplines="
    .42 0 1 1;
    0 0 .59 1;
    .42 0 1 1;
    0 0 .59 1;
    .42 0 1 1;
    0 0 .59 1;
    .42 0 1 1;
    0 0 .59 1;" 
  fill="freeze" 
  id="circ-anim">
</animate>

这是现场演示:

如果你只想为整个动画指定一个整体缓动函数而没有任何中间值,你仍然必须使用该keyTimes属性指定关键帧,但你只需指定开始和结束关键帧,即0; 1,而没有中间值values

加法和累积动画:additiveaccumulate

有时,定义一个从前一个动画结束的地方开始的动画很有用;或使用先前动画的累积总和作为继续执行的值的动画。为此,SVG 有两个方便命名的属性:additiveaccumulate.

假设你有一个要“增加”宽度的元素,或者要增加一条线的长度,或者要在不同的步骤中逐步从一个位置移动到另一个位置的元素。此功能对于重复动画特别有用。

就像任何其他动画一样,你将指定fromto值。但是,当你设置additive为时sum,它们的每个值都将相对于动画属性的原始值

那么,回到我们的圈子。对于我们的圆,初始位置cx是50,当你设置from="0" to="100"的时候,0其实就是原来的50,100其实就是50+100;换句话说,它实际上有点像“ from="50" to="150"”。

通过这样做,我们得到以下结果:

这就是additive属性的全部作用。它只是指定fromto值是否应该相对于当前值。该属性仅采用以下两个值之一:sumreplace。后者是默认值,它基本上意味着fromto值将替换当前/原始值,这最终可能会在动画开始之前导致奇怪的跳跃。(尝试在上面的示例中替换sumreplace以获得更好的比较。)

但是,如果我们希望添加的值使得第二次重复从前一次的结束值开始怎么办?这就是accumulate属性的用武之地。

accumulate属性控制动画是否累积。默认值为none,这意味着,例如,当动画重复时,它将从头开始。但是,你可以将其设置为sum,这指定在第一次迭代之后的每个重复迭代都建立在前一次迭代的最后一个值之上。

所以,如果我们回到之前的动画并指定accumulate="sum",我们将得到以下可预知的结果:
https://codepen.io/SaraSoueidan/pen/zYeaGp/e21b7dd0af3d0a6db2828362bee24d48

请注意,accumulate如果目标属性值不支持添加,或者如果动画元素不重复,则忽略该属性。to如果仅使用属性指定动画功能,它也会被忽略。

指定动画的结束时间end

除了指定动画何时开始外,你还可以使用end属性指定动画何时结束。例如,你可以将动画设置为无限重复,然后在另一个元素开始动画时停止。属性采用的end值类似于begin值采用的值。你可以指定绝对或相对时间值/偏移量、重复值、事件值等。

例如,在下面的演示中,橙色圆圈在 30 秒内缓慢移动到画布的另一侧。绿色圆圈也会动画,但只有在单击时才会动画。当绿色圆圈的动画开始时,橙色圆圈的动画将结束。点击绿色圆圈可以看到橙色的一站式:

当然,可以为应用于同一元素的两个动画实现相同类型的动画同步。例如,假设我们将圆圈的颜色设置为从一个值无限期地变为另一个值的动画。然后,当单击该元素时,它会移动到另一侧。我们现在将设置它,以便在单击元素并触发移动动画时立即停止颜色动画。

begin使用多个和end值定义动画间隔

实际上,beginend属性都接受以分号分隔的值列表。属性中的每个值begin都会对应属性中的一个值end,从而形成活跃和不活跃的动画区间。

你可以将其想象成类似于行驶中的汽车,汽车的轮胎会在一段时间内处于活动状态,然后在一段时间内处于不活动状态,具体取决于汽车是否在移动。你甚至可以通过将动画应用到汽车来创建动画汽车效果:一个平移汽车或沿着路径移动它也是一种累加和累积动画,另一个动画以同步的间隔旋转汽车轮胎与翻译。

指定多个开始和结束时间(即间隔)的示例是以下演示,其中矩形根据定义的间隔旋转,相应地从活动变为非活动。(如果你错过了动画,请重新运行演示。)

请注意,在上面的示例中,我使用了元素来围绕其中心旋转矩形。我们将在下面的下一节中更详细地讨论这个元素。

另请注意,即使你设置repeatCountindefinite,它也会被end值覆盖并且不会无限期地重复。

min使用和限制元素的活动持续时间max

就像你可以限制动画的重复时间一样,你甚至可以限制动画的活动持续时间min和属性分别指定活动持续时间的max最小值和最大值。它们为我们提供了一种控制元素活动持续时间下限和上限的方法。这两个属性都以时钟值作为值。

对于min,它指定活动持续时间的最小值的长度,以元素活动时间测量。值必须大于或等于 0,这是默认值,根本不限制活动持续时间。

对于max,时钟值指定活动持续时间最大值的长度,以元素活动时间测量。值也必须大于 0。默认max值为indefinite。这根本不限制活动持续时间。

如果同时指定了minmax属性,则该max值必须大于或等于该min值。如果不满足此要求,则忽略这两个属性。

但是什么定义了元素的活动持续时间? 之前我们提到了重复持续时间,除了“简单持续时间”之外,也就是没有任何重复的动画持续时间(使用 指定dur),那么所有这些是如何协同工作的呢?哪个覆盖什么?那么end将覆盖并简单地结束动画的属性呢?

它发生的方式是浏览器将首先根据 、 、 和 值计算dur活动repeatCount持续repeatDur时间end然后min,它根据指定的和max值运行计算的持续时间。如果结果在范围内,则此第一个计算的持续时间值是正确的并且不会更改。否则可能会出现两种情况:

  • 如果第一个计算的持续时间大于该max值,则元素的活动持续时间被定义为等于该max值。

  • 如果第一个计算的持续时间小于该min值,则元素的活动持续时间变为等于该min值并且该元素的行为如下:

    • 如果元素的重复持续时间(如果元素不重复,则为简单持续时间)大于,min则该元素将在(min受限的)活动持续时间内正常播放。
    • 否则,元素将在其重复持续时间(如果元素不重复则为简单持续时间)内正常播放,然后根据fill属性值冻结或不显示。

这让我们知道浏览器实际上是如何计算活动持续时间的。为了简洁起见,我不打算在这里详细介绍。但是规范中有一个非常全面的表格,显示了 、 、 和 属性的不同组合durrepeatCount然后repeatDur显示end了基于每个组合的活动持续时间。你可以查看该表并在规范的这一部分阅读更多相关信息。

最后,如果一个元素被定义为在其父元素之前开始(例如,使用简单的负偏移值),则最小持续时间是从计算的开始时间而不是观察到的开始时间开始测量的。这意味着该min值可能没有观察到的效果。

示例:变形路径

可以在 SMIL 中(但不能在 CSS 中)设置动画的属性之一是 SVG 的d属性(data的缩写)。该d属性包含定义你正在绘制的形状轮廓的数据。路径数据由一组命令和坐标组成,这些命令和坐标告诉浏览器在哪里以及如何绘制构成最终路径的点、弧和线。

为该属性设置动画允许我们变形SVG 路径并创建形状补间效果。但是,为了能够变形形状,开始、结束和任何中间路径形状需要具有完全相同数量的顶点/点,并且它们需要以相同的顺序出现。如果顶点数量不匹配,动画将无法运行。这样做的原因是形状的改变实际上是通过移动顶点并插值它们的位置而发生的,所以如果一个顶点丢失或不匹配,路径将不再被插值。

要为 SVG 路径设置动画,你可以将 指定attributeNamed,然后设置指定开始和结束形状的fromto值,并且你可以使用该values属性指定你希望形状在其间经过的任何中间值。

为了简洁起见,我不会在这里详细介绍如何执行此操作。相反,你可以阅读Noah Blon 撰写的这篇优秀文章,其中他解释了他如何使用 . 诺亚文章的现场演示是这样的:

https://codepen.io/noahblon/pen/wvxmgv

这是 Felix Hornoiu 的另一个变形示例:

https://codepen.io/felixhornoiu/pen/JjmVZw

你甚至可以变形用作剪贴蒙版的路径的值!Heather Buchel 的一个例子:

https://codepen.io/hbuchel/pen/YzYMgd

沿任意路径制作动画:元素

该元素是我最喜欢的 SMIL 动画元素。你可以使用它沿路径移动元素。你可以使用我们接下来要介绍的两种方法之一来指定运动路径,然后设置元素,使其沿着该路径移动。

该元素接受前面提到的相同属性,外加三个属性:keyPointsrotatepath。此外,该属性也有一个区别calcMode,默认值为paced,而不是linear

path使用属性指定运动路径

path属性用于指定运动路径。它以与元素d上的属性相同的格式表示并以相同的方式解释。path运动路径动画的效果是将补充变换矩阵添加到引用对象的当前变换矩阵上,这会导致沿当前用户坐标系的 x 轴和 y 轴平移计算出的 X 和 Y 值时间。换句话说,指定的路径是相对于元素的当前位置计算的,通过使用路径数据将元素转换到路径位置。

对于我们的圆圈,我们将沿着如下所示的路径对其进行动画处理:

圆圈沿着这条路径移动所需的代码是:

<animatemotion xlink:href="#circle" dur="1s" begin="click" 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.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"></animatemotion>

我想在这里关注一件事:路径数据中的坐标。该路径首先将 ( M) 移动到坐标为 的点(0, 0),然后开始绘制一条曲线 ( c) 到另一点。重要的是要注意,该(0, 0)点实际上是圆的位置,无论它在哪里——而不是坐标系的左上角。上面我们说过,path属性中的坐标是相对于元素当前位置的!

上面代码的结果是:
https://codepen.io/SaraSoueidan/pen/YzBJxb/184082960ac3cc65d00b22f2551a330a

如果你要指定从点以外的点开始的路径(0, 0),圆将突然跳跃起点中指定的量。例如,假设你在 Illustrator 中绘制一条路径,然后导出该路径数据以用作运动路径(这是我第一次这样做);导出的路径可能看起来像这样:

<path fill="none" stroke="#000000" stroke-miterlimit="10" d="M100.4,102.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"></path>

本例中路径的起点是(100.4, 102.2)。如果我们使用此数据作为运动路径,圆将向右跳约 100 个单位并向下跳约 102 个单位,然后开始沿着相对于新位置的路径运动。因此,当你为动画准备运动路径时,请务必牢记这一点。

如果使用,属性fromby,tovalues在当前画布上指定一个表示运动路径的形状。

使用元素指定运动路径

还有另一种方法可以指定运动路径。path你可以使用元素引用外部路径,而不是使用相对属性。然后,该元素的子元素将使用该xlink:href属性引用外部路径。

<animatemotion xlink:href="#circle" dur="1s" begin="click" fill="freeze">
 <mpath xlink:href="#motionPath"></mpath>
</animatemotion>

运动路径可以在文档的任何地方定义;它甚至可以从字面上只定义在一个元素内,根本不在画布上呈现。在下一个示例中,将呈现路径,因为在大多数情况下,你可能希望显示元素移动的路径。

请注意,根据规范:

形状的各种 (x, y) 点为参考对象提供了一个补充变换矩阵到 CTM 上,它导致沿当前用户坐标系的 x 和 y 轴平移 (x,y) 值随时间计算的形状。因此,参考对象随时间平移运动路径相对于当前用户坐标系原点的偏移量。由于目标元素的transform属性或由于animateTransform目标元素上的元素而在该属性上的任何动画,补充转换应用于任何转换之上。

同样,圆的位置与路径数据中的坐标“相乘”或“转换”。

在下一个示例中,我们在画布中间有一条路径。圆圈位于路径的开头。然而,当应用运动路径时,圆不会从其当前位置开始运动。请参阅演示以获得更好的解释。单击圆圈使其动画化。

看看圆是如何遵循相同形状的路径,但在不同的位置?这是由于圆的位置由路径数据的值转换所致。

解决此问题的一种方法是从位于 的圆开始(0, 0),这样当使用路径数据对其进行转换时,它将按预期开始并继续进行。

另一种方法是应用“重置”圆坐标的转换,以便在应用路径之前它们计算为零。

以下是上述演示的修改版本,使用闭合路径并无限重复运动动画。

https://codepen.io/SaraSoueidan/pen/ExrdEe/ef9f0e1242263cf23067b09be894cfa9

覆盖规则<animatemotion></animatemotion>

由于有不止一种方法可以为 做同样的事情animateMotion,所以只有覆盖规则来指定哪些值覆盖其他值才有意义。

的覆盖规则animateMotion如下:

  • 关于运动路径的定义,mpath元素覆盖了path属性,覆盖了values,覆盖了frombyto
  • keyTimes关于确定属性对应的点,keyPoints属性覆盖path,覆盖values,覆盖frombyto

沿运动路径设置元素的方向rotate

在我们前面的例子中,我们沿着路径设置动画的元素恰好是一个圆。但是,如果我们正在为具有特定方向的元素(例如汽车图标)制作动画怎么办?以下示例中的汽车图标由 Freepik 设计

在此示例中,我将圆圈替换为 ID 为“car”的组,其中包含组成该组的元素。然后,为了避免上述路径上的运动问题,我对汽车应用了一个转换,使其平移特定的量,以便初始位置结束于 (0, 0)。转换中的值实际上是汽车的第一条路径开始绘制的点的坐标(就在移动命令M之后)。

然后汽车开始沿着运动路径移动。但是……这就是运动的样子:

https://codepen.io/SaraSoueidan/pen/poGxKW/3a300b8c4c0f9db4ff345f5d44992b74

汽车的方向是固定的,不会改变以匹配运动路径的方向。为了改变这一点,我们将使用该rotate属性。

rotate属性采用以下三个值之一:

  • auto:表示物体随时间旋转运动路径方向(即方向切向量)的角度。
  • auto-reverse:表示物体随时间旋转运动路径的方向(即方向切向量)的角度加上180度。
  • 数字:指示目标元素应用了恒定旋转变换,其中旋转角度是指定的度数。

为了在上面的例子中固定汽车的方向,我们首先将旋转值设置为auto。我们最终会得到以下结果:

https://codepen.io/SaraSoueidan/pen/LYqgra/74af0bd0bbc7ca46d4d568ca0d473b40

如果你希望汽车移动到路径之外,该auto-reverse值会解决这个问题。

https://codepen.io/SaraSoueidan/pen/gOqBja/1027d099f0e9cca94f8f8865d169c49f

这看起来好多了,但我们仍然有一个问题:汽车看起来像是在沿着路径向后移动!为了改变这种情况,我们需要沿 y 轴翻转汽车。这可以通过沿该轴将其缩放“-1”倍来完成。因此,如果我们将转换应用于g具有carID 的 ,汽车将按预期向前移动。缩放转换将与我们之前应用的先前转换链接在一起。

最后的演示是这样的:

https://codepen.io/SaraSoueidan/pen/rNPqrK/48caf2f5fa42a8c154fcb5dec0dbe4d5

控制沿运动路径的动画距离keyPoints

该属性提供了为每个指定值keyPoints指定沿运动路径的进度的能力。keyTimes如果指定,则keyPoints导致keyTimes应用于属性数组中指定的值keyPoints而不是values属性数组中指定的点或属性上的点path

keyPoints``keyTimes采用分号分隔的 0 到 1 之间的浮点值列表,并指示对象在相应值指定的时刻应沿着运动路径移动多远。距离计算由浏览器的算法决定。列表中的每个进度值对应于keyTimes属性列表中的一个值。如果keyPoints指定了列表,则列表中的值必须与keyPoints列表中的值一样多keyTimes

这里要注意的一件重要事情是将calcMode值设置为linearfor keyPointsto work。如果你的关键点来回移动,它看起来也应该在逻辑上与节奏动画一起工作,但事实并非如此。

以下是 Amelia Bellamy-Royds 的示例(你应该完全查看其CodePen 配置文件keyPoints),该示例用于模拟行为是从预定义的偏移量开始沿路径运动,因为我们目前没有这种能力SMIL 中的默认值。

https://codepen.io/AmeliaBR/pen/VwNvpw

沿任意路径移动文本

沿任意路径移动文本不同于沿路径移动其他 SVG 元素。要为文本设置动画,你将不得不使用元素,而不是元素。

首先,让我们从沿着路径定位文本开始。这可以通过在元素内嵌套一个元素来完成。将要沿路径定位的文本将在元素内部定义,而不是作为元素的子元素。

然后textPath将引用我们要使用的实际路径,就像我们在前面的示例中所做的那样。引用路径也可以呈现在画布上,或在 . 把下面的demo中的代码查出来。
https://codepen.io/SaraSoueidan/pen/NWoOEp/ebfc92e45e24b29c266f50e6f617cdf5

为了沿该路径为文本设置动画,我们将使用元素为startOffset属性设置动画。

startOffset表示文本在路径上的偏移量。0% 是路径的开始;100%代表它的结束。因此,例如,如果偏移量设置为 50%,文本将从路径的中途开始。我想你可以从这里看到我们要去的地方。

通过为 设置动画startOffset,我们将创建文本沿路径移动的效果。把下面的demo中的代码查出来。

https://codepen.io/SaraSoueidan/pen/qBgJLw/501308e154923359ed1cdbfa29eadcc0

动画转换:<animatetransform></animatetransform>元素

该元素为目标元素上的转换属性设置动画,从而允许动画控制平移、缩放、旋转和/或倾斜。它采用与元素相同的属性,外加一个附加属性:type

type属性用于指定动画转换的类型。它采用以下五个值之一:translatescalerotateskewXskewY

,from和属性采用byto给定转换类型可用的相同语法表示的值:

  • 对于 a type="translate",每个单独的值表示为<tx> [,<ty>]</ty></tx>
  • 对于 a type="scale",每个单独的值表示为<sx> [,<sy>]</sy></sx>
  • 对于 a type="rotate",每个单独的值表示为<rotate-angle> [<cx> <cy>]</cy></cx></rotate-angle>
  • 对于 atype="skewX"type="skewY",每个单独的值表示为<skew-angle></skew-angle>

如果你不熟悉 SVGtransform属性函数的语法,并且为了本文的简洁起见,并且因为语法详细信息及其工作原理超出了本文的范围,我建议你阅读我的文章在继续阅读本指南之前,有一段时间写过这篇文章:“理解 SVG 坐标系和变换(第 2 部分):transform属性”

回到之前的演示,我们使用<animatetransform></animatetransform>元素旋转粉色矩形。旋转的代码如下所示:

<rect id="deepPink-rectangle" width="50" height="50" x="50" y="50" fill="deepPink">
  <animatetransform 
    xlink:href="#deepPink-rectangle" 
    attributename="transform" 
    attributetype="XML" 
    type="rotate" 
    from="0 75 75" 
    to="360 75 75" 
    dur="2s" 
    begin="0s" 
    repeatcount="indefinite" 
    fill="freeze"
  >
</rect>

和属性指定旋转角度(开始和结束)和旋转中心fromto当然,在两者中,旋转中心保持不变。如果你不指定中心,它将位于 SVG 画布的左上角。上述代码的现场演示如下:
https://codepen.io/SaraSoueidan/pen/OJdBqR/1bb859d4103d5e32b037f69e906319fb

animateTransform这是Gabriel的单曲的另一个有趣示例:
https://codepen.io/guerreiro/pen/rNZajZ

为单个变换设置动画很简单,但是,当包含多个变换时,事情会变得非常混乱和复杂,特别是因为一个animateTransform可以覆盖另一个,所以不是添加和链接效果,你最终可能会得到完全相反的结果。那,除了 SVG 坐标系和转换的实际工作方式(请参阅前面提到的关于该主题的文章)。这些例子很多,超出了本文的范围。对于转换 SVG,我建议使用 CSS 转换。实现正在努力使后者与 SVG 完美配合,因此你可能根本不需要使用 SMIL 来为 SVG 中的转换设置动画。

<set></set>元素_

set元素提供了一种在指定持续时间内设置属性值的简单方法。它支持所有属性类型,包括那些不能合理插入的属性类型,例如字符串和布尔值。该set元素是非添加的。additive 和 accumulate 属性是不允许的,如果指定将被忽略。

由于用于在特定时间和期间将元素设置特定值,因此它不接受前面提到的动画元素的所有属性。比如它没有a fromorby属性,因为变化的值不会随着时间的推移递增变化。

对于set,你可以指定你要定位的元素、属性名称和类型、to值,并且动画时间可以通过以下方式控制:begindurendminmaxrestartrepeatCountrepeatDurfill

以下是将旋转矩形的颜色设置为单击时的蓝色的示例。颜色保持蓝色 3 秒,然后变回原来的颜色。每次单击矩形时,set都会触发动画,并更改颜色三秒钟。
https://codepen.io/SaraSoueidan/pen/xxMyoe/af159baaf57bc38eb40288db722e1245

可以设置动画的元素、属性和属性

并非所有 SVG 属性都可以设置动画,也不是所有可以设置动画的属性都可以使用所有动画元素设置动画。有关所有动画属性的完整列表,以及显示哪些属性可以由哪些元素动画的表格,请参阅SVG 动画规范的这一部分

最后的话

SMIL 有很大的潜力,我只是触及皮毛,只触及了它们在 SVG 中如何工作的基础知识和技术细节。可以创建许多非常令人印象深刻的效果,尤其是涉及变形和变换形状的效果。天空是极限。疯了!并且不要忘记与社区分享你的作品;我们很想看看你在做什么。感谢你的阅读!

文章翻译自:https://css-tricks.com/guide-svg-animations-smil/

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容