2015 年 12 月更新:在此更新时,SMIL 似乎正在逐渐消亡。Sarah Drasner 提供了一份指南,介绍如何替换其中一些功能。
以下是 Sara Soueidan 的一篇客座文章。Sara 擅长深入挖掘 Web 功能,并为我们其他人解释清楚。在这里,她深入探讨了 SMIL(以及相关内容),以及直接构建到 SVG 中的动画语法,并为我们提供了这份史诗般的指南。
目录
概述
SVG 图形可以使用 动画元素 进行动画处理。动画元素最初是在 SMIL 动画规范中定义的;这些元素包括
<animate></animate>
– 允许您在一段时间内对标量属性进行动画处理。<set></set>
– 是 animate 的一种简便速记,对于将动画值分配给非数字属性非常有用,例如 visibility 属性。<animatemotion></animatemotion>
– 使元素沿着运动路径移动。<animatecolor></animatecolor>
– 会随着时间推移修改特定属性的色彩值。请注意,该元素已被弃用,建议使用 animate 元素来针对可以接受色彩值的属性。尽管它仍然存在于 SVG 1.1 规范中,但明确指出它已被弃用;并且在 SVG 2 规范中已完全删除。
除了 SMIL 规范中定义的动画元素外,SVG 还包含与 SMIL 动画规范兼容的扩展;这些扩展包括扩展元素功能的属性和附加动画元素。SVG 扩展包括
<animatetransform></animatetransform>
– 允许您随着时间推移对 SVG 的某个变换属性进行动画处理,例如 transform 属性。path
(属性) – 允许在animateMotion
元素的 path 属性中指定 SVG 路径数据语法的任何功能(SMIL 动画只允许在 path 属性中使用 SVG 路径数据语法的子集)。我们将在下一节中详细讨论animateMotion
。<mpath></mpath>
– 与animateMotion
元素结合使用,用于引用要用作运动路径的运动路径。mpath 元素包含在animateMotion
元素内,位于结束标签之前。keypoints
(属性) – 用作animateMotion
的属性,用于精确控制运动路径动画的速度。rotate
(属性) – 用作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 Baranovsky 的 snap.svg,它被称为“SVG 的 jQuery”。以下是 示例集合。
或者,如果您更喜欢更具声明性的动画方法,可以使用 SVG 动画元素,如本指南中所述!
与 JS 动画相比,SMIL 另一个优势是 JS 动画在 SVG 嵌入为 img
或用作 CSS 中的 background-image
时不起作用。SMIL 动画在这两种情况下都起作用(或应该起作用,具体取决于浏览器支持)。在我看来,这是一个很大的优势。您可能会发现自己因为这个原因而选择 SMIL 而不是其他选项。本文是一份指南,可以帮助您立即开始使用 SMIL。
浏览器支持和回退
对 SMIL 动画的浏览器支持相当不错。它们在除 Internet Explorer 和 Opera Mini 之外的所有浏览器中都能正常工作。有关浏览器支持的详细概述,您可以参考 Can I Use 上的兼容性表。
如果您需要为 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 轴上的位置进行动画处理,则可以通过将 cx
指定为 attributeName
属性的值来实现。
attributeName
只接受一个值,而不是一个值列表,因此,您一次只能对一个属性进行动画处理。如果您想对多个属性进行动画处理,则需要为该元素定义多个动画。我希望这一点有所不同,我认为 CSS 在这一点上比 SMIL 更有优势。但另一方面,由于其他动画属性可能具有值(我们将在下一节中介绍),因此一次只定义一个属性名称是合理的,否则其他属性值将过于复杂而难以使用。
当您指定属性名称时,可以添加一个 XMLNS(XML 命名空间的缩写)前缀来指示该属性的命名空间。也可以使用 attributeType
属性指定命名空间。例如,有些属性是 CSS 命名空间的一部分(这意味着该属性也可以作为 CSS 属性找到),而另一些属性则是纯 XML 属性。您可以从 这里 找到展示这些属性的表格。该表格中的属性并非所有 SVG 属性,而是那些可以使用 CSS 设置的属性。其中一些属性已经作为 CSS 属性存在。
如果 attributeType
的值没有明确设置或设置为 auto
,则浏览器必须首先在 CSS 属性列表中搜索匹配的属性名称,如果未找到,则在元素的默认 XML 命名空间中搜索。
例如,以下代码段会对 SVG 矩形的 opacity
属性进行动画。由于 opacity
属性也可用作 CSS 属性,因此将 attributeType
设置为 CSS 命名空间
<rect>
<animate
attributetype="CSS"
attributename="opacity"
from="1"
to="0"
dur="5s"
repeatcount="indefinite">
</animate>
</rect>
在以下的示例中,我们将介绍其他动画属性。除非另有说明,否则所有动画属性对所有动画元素都通用。
from
、by
、to
、dur
和 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
属性类似于 CSS 中的 animation-duration
等效属性。
from
和 to
属性类似于动画的 @keyframe
块中 CSS 的 from
和 to
关键帧。
@keyframes moveCircle {
from { /* start value */ }
to { /* end value */ }
}
fill
属性(不幸的是与定义元素填充颜色的 fill
属性同名)类似于 animation-fill-mode
属性,该属性指定动画结束后元素是否应恢复到其初始状态。SVG 中的值与 CSS 中的值类似,只是使用了不同的名称。
freeze
:动画效果被定义为在活动持续时间的最后一个值处 冻结 效果值。动画效果在文档持续时间的剩余时间内被“冻结”(或直到动画重新启动)。remove
:动画效果在动画的活动持续时间结束后将被移除(不再应用)。在动画的活动结束之后,动画不再影响目标(除非动画重新启动)。
尝试在实时演示中更改这些值,以查看动画是如何受到影响的。
by
属性用于指定动画的相对偏移量。顾名思义,您可以使用它来指定动画的 前进 程度。by
的效果几乎只在您以离散步骤跨越动画持续时间时可见,类似于它在 CSS 的 steps()
函数中工作的方式。SVG 中 CSS steps()
函数的等效函数是 calcMode="discrete"
。我们将在本文稍后的部分介绍 calcMode
属性。
by
的效果更明显的情况是,当您只指定 to
属性时。例如,如果您在本文后面将要介绍的 set
元素中使用它,就会出现这种情况。
最后但并非最不重要的一点是,by
在您使用加法和累积动画时也很有用。我们将在本文稍后的部分介绍。
restart
重新启动动画
使用 在动画处于活动状态时阻止动画重新启动可能很有用。为此,SVG 提供了 restart
属性。您可以将此属性设置为三个可能值之一。
always
:动画可以随时重新启动。这是默认值。whenNotActive
:动画只能在非活动状态(即活动结束之后)重新启动。在动画的活动持续时间内尝试重新启动动画将被忽略。never
:元素在父时间容器的当前简单持续时间的剩余时间内无法重新启动。(在 SVG 的情况下,由于父时间容器是 SVG 文档片段,因此动画在文档持续时间的剩余时间内无法重新启动。)
命名动画和同步动画
假设我们要对圆圈的位置 和 颜色进行动画,以便颜色的变化在移动动画结束时发生。我们可以通过将颜色变化动画的 begin
值设置为移动动画的 dur
ation 来实现这一点;这正是我们通常在 CSS 中的做法。
然而,SMIL 具有一个很好的事件处理功能。我们之前提到过,begin
属性接受 click + 5s
之类的值。此值称为“事件值”,在本例中由事件引用后跟一个“时钟值”组成。这里有趣的是第二部分的命名:“时钟值”。为什么不是简单的“时间值”?答案是,您可以使用像“10min”或“01:33”这样的 时钟值,它们等效于“1 分钟 33 秒”,甚至“02:30:03”(2 小时 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 秒开始矩形的动画。您可以查看实时演示。
您也可以在圆圈动画结束之后开始矩形动画,方法是使用 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: alternate
值意味着奇数次循环迭代以正常方向播放,偶数次循环迭代以反向播放。这意味着第一个循环将从头到尾播放,然后第二个循环将从尾到头播放,然后第三个循环将从头到尾播放,依此类推。
在 SMIL 中,要做到这一点,您需要使用 JavaScript 明确地更改 from
和 to
属性的值。Big Bite Creative 的 Jon McPartland 写了一篇文章,解释了他如何为一个菜单图标动画做到这一点。
另一种解决方法是将结束值指定为中间值,然后将结束值设置为与初始值相同。例如,您可以将动画设置为从某个值开始,from
,并以与 to
相同的值结束,但您将指定您本来设置的最终值,作为 from
和 to
之间的中间值。
在 CSS 中,我们可以使用类似下面的代码来实现这一点
@keyframes example {
from, to {
left: 0;
}
50% {
left: 300px;
}
}
SMIL 中的等效代码是使用 values
属性,我们稍后会解释。
也就是说,上述解决方法可能对您有用,也可能对您没有用,具体取决于您想要实现的动画类型,以及您是否正在链接动画、重复动画或进行累加动画。
以下是一个由 Miles Elam 提供的漂亮、简单的无限动画,使用了一些延迟的开始时间
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 秒开始。
还有一个由 David Eisenberg 为SVG Essentials(第 2 版)制作的示例 点击这里查看。
keyTimes
和 values
控制动画关键帧值:在 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
属性值都将被忽略。
在此,还要提一下,您可以在没有 keyTimes
属性的情况下使用 values
属性——这些值会自动均匀地分布在时间范围内(对于除 paced
之外的所有 calcMode
值(见下一节)。
calcMode
和 keySplines
使用自定义缓动控制动画节奏:我将再次进行 CSS-SMIL 对比,因为如果您已经熟悉 CSS 动画,那么 SMIL 的语法和概念会更容易理解。
在 CSS 中,您可以选择更改默认的均匀动画节奏,并指定一个自定义缓动函数来控制动画,使用 animation-timing-function
属性。缓动函数可以是几个预定义的关键字之一,或者是一个 三次贝塞尔 函数。后者可以使用像 这个 由 Lea Verou 提供的工具来创建。
在 SMIL 中,动画节奏是使用 calcMode
属性指定的。除了 animateMotion
(我们将在本文后面讨论),所有动画元素的默认动画节奏都是 linear
。除了 linear
值之外,您还可以将该值设置为:discrete
、paced
或 spline
。
discrete
指定动画函数将从一个值跳到下一个值,没有任何插值。这类似于 CSS 中的steps()
函数。paced
类似于linear
,只是它会忽略keyTimes
定义的任何中间进度时间。它计算出后续值之间的距离,并将时间相应地划分。如果您的值都是线性顺序的,您将不会注意到区别。但如果它们来回移动,或者它们是颜色(被视为三维向量值),您一定会看到中间值。这是 Amelia Bellamy-Royds 提供的一个演示,展示了迄今为止提到的三个calcMode
值之间的区别。calcMode
接受的第四个值是spline
。它根据由三次贝塞尔样条曲线定义的时间函数,在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;
}
}
除了关键字缓动函数之外,我们还可以使用相应的三次贝塞尔函数
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-in
和 ease-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
,而不需要任何中间 值
。
additive
和 accumulate
叠加与累积动画:有时,定义一个从前一个动画结束的地方开始的动画或一个使用前一个动画的累积总和作为值来进行的动画非常有用。为此,SVG 有两个方便命名的属性:additive
和 accumulate
。
假设你有一个元素,你想让它的宽度“增长”,或者一条线,你想让它的长度增加,或者一个元素,你想让它逐步从一个位置移动到另一个位置,在不同的步骤中完成。此功能对于重复动画特别有用。
就像任何其他动画一样,你将指定 from
和 to
值。但是,当你将 additive
设置为 sum
时,它们的每个值都将 **相对于动画属性的原始值**。
所以,回到我们的圆形。对于我们的圆形,cx
的初始位置为 50。当您设置 from="0"
to="100"
时,零实际上是原始的 50,而 100 实际上是 50 + 100;换句话说,它实际上有点像 “from="50" to="150"
“。
通过这样做,我们将获得以下结果。
这就是 additive
属性的作用。它只是指定 from
和 to
值是否应该相对于当前值。该属性只接受两个值之一:sum
和 replace
。后者是默认值,它基本上意味着 from
和 to
值将替换当前/原始值,这可能会导致动画开始前出现奇怪的跳跃。(尝试在上面的示例中将 sum
替换为 replace
以更好地进行比较。)
但是,如果我们希望将值相加,以便第二次重复从上一次重复的结束值开始呢?这就是 accumulate
属性发挥作用的地方。
accumulate
属性控制动画是否累积。默认值为 none
,这意味着例如当动画重复时,它将从头开始。但是,你可以将其设置为 sum
,它指定除了第一次之外的每次重复迭代都将建立在上一次迭代的最后一个值之上。
因此,如果我们回到之前的动画并指定 accumulate="sum"
,我们将获得以下更理想的结果。
请注意,如果目标属性值不支持加法,或者动画元素不重复,则 accumulate
属性将被忽略。如果动画函数仅使用 to
属性指定,它也将被忽略。
end
指定动画的结束时间
使用 除了指定动画何时开始之外,你还可以使用 end
属性指定动画何时结束。例如,你可以将动画设置为无限重复,然后在另一个元素开始动画时使其停止。end
属性接受与 begin
值类似的值。你可以指定绝对或相对时间值/偏移量、重复值、事件值等。
例如,在以下演示中,橙色圆圈在 30 秒内缓慢移动到画布的另一侧。绿色圆圈也会动画,但只有在点击它时才会动画。橙色圆圈的动画将在绿色圆圈的动画开始时结束。点击绿色圆圈以查看橙色圆圈停止。
当然,对于应用于同一元素的两个动画,也可以实现相同类型的动画同步。例如,假设我们设置圆圈的颜色以无限重复地从一个值更改为另一个值。然后,当元素被点击时,它会移动到另一侧。现在我们将设置它,以便颜色动画在元素被点击并触发移动动画时停止。
begin
和 end
值定义动画间隔
使用多个 实际上,begin
和 end
属性都接受 **用分号分隔的列表**。begin
属性中的每个值都将对应于 end
属性中的一个值,从而形成活动和非活动动画间隔。
你可以将其视为类似于一辆移动的汽车,汽车的轮胎会在一段时间内处于活动状态,然后处于非活动状态,具体取决于汽车是否在移动。你甚至可以通过对汽车应用两个动画来创建动画汽车效果:一个动画平移汽车或使其沿着路径移动,该路径也是一个叠加和累积动画,另一个动画以与平移同步的间隔旋转汽车的轮胎。
以下演示示例指定了多个开始和结束时间(即间隔),其中矩形根据定义的间隔旋转,相应地从活动变为非活动。(如果你错过了动画,请重新运行演示。)
请注意,在上面的示例中,我使用 元素绕其中心旋转矩形。我们将在下面的下一节中详细讨论此元素。
还要注意,即使你将 repeatCount
设置为 indefinite
,它也会被 end
值覆盖,并且 *不会* 无限重复。
min
和 max
限制元素的活动持续时间
使用 就像你可以限制动画的重复时间一样,你甚至可以限制动画的 **活动持续时间**。min
和 max
属性分别指定活动持续时间的最小值和最大值。它们为我们提供了一种控制元素活动持续时间的下限和上限的方法。这两个属性都接受时钟值作为值。
对于 min
,它指定活动持续时间的最小值的长度,以元素活动时间测量。值必须大于或等于 0,这是默认值,并且不会对活动持续时间造成任何约束。
对于 max
,时钟值指定活动持续时间的最大值的长度,以元素活动时间测量。值也必须大于 0。max
的默认值为 indefinite
。这不会对活动持续时间造成任何约束。
如果同时指定 min
和 max
属性,则 max
值必须大于或等于 min
值。如果不满足此要求,则这两个属性都会被忽略。
但是,什么定义了元素的 **活动持续时间**?我们之前提到了重复持续时间,除了“简单持续时间”之外,它指的是不带任何重复的动画持续时间(使用 dur
指定),那么它们如何协同工作?哪个会覆盖哪个?然后,end
属性会覆盖并简单地结束动画吗?
发生的方式是,浏览器将 *首先* 根据 dur
、repeatCount
、repeatDur
和 end
值计算活动持续时间。*然后*,它将计算出的持续时间与指定的 min
和 max
值进行比较。如果结果在边界内,则此第一个计算出的持续时间值是正确的,并且不会更改。否则,可能会出现两种情况
- 如果第一个计算的持续时间大于
max
值,则元素的活动持续时间定义为等于max
值。 - 如果第一个计算的持续时间小于
min
值,则元素的活动持续时间将变为等于min
值,并且元素的行为如下:- 如果元素的重复持续时间(或元素不重复的简单持续时间)大于
min
,则元素会以正常方式播放(min
约束的)活动持续时间。 - 否则,元素会以正常方式播放其重复持续时间(或元素不重复的简单持续时间),然后根据
fill
属性的值冻结或不显示。
- 如果元素的重复持续时间(或元素不重复的简单持续时间)大于
这让我们回到了浏览器如何实际计算活动持续时间的问题。为了简洁起见,这里不详细说明。但在规范中有一个非常全面的表格,其中显示了dur
、repeatCount
、repeatDur
和end
属性的不同组合,然后根据每种组合显示活动持续时间。您可以查看表格,并从规范的这一部分了解更多信息。
最后,如果元素定义为在其父元素之前开始(例如,使用简单的负偏移值),则最小持续时间是从计算的开始时间而不是观察到的开始时间开始计算。这意味着min
值可能没有观察到的效果。
示例:路径变形
SMIL(但不在 CSS 中)可以动画化的属性之一是 SVG 的d
属性(代表数据)。d
属性包含定义要绘制的形状轮廓的数据。路径数据由一系列命令和坐标组成,这些命令和坐标告诉浏览器在哪里以及如何绘制构成最终路径的点、弧线和线。
动画化此属性使我们能够变形 SVG 路径并创建形状补间效果。但是,为了能够变形形状,起始形状、结束形状和任何中间形状都需要具有完全相同的顶点/点数,并且它们需要以相同的顺序出现。如果顶点数不匹配,动画将无法正常工作。原因是形状改变实际上是通过移动顶点和插值其位置来实现的,因此如果一个顶点缺失或不匹配,路径将不再被插值。
要动画化 SVG 路径,您需要将attributeName
指定为d
,然后设置from
和to
值来指定起始形状和结束形状,并且可以使用values
属性来指定您希望形状在两者之间经过的任何中间值。
为了简洁起见,这里不详细说明如何执行此操作。您可以阅读Noah Blon 的这篇优秀文章,其中他解释了他是如何使用 SMIL 创建了一种形状补间式加载动画的。Noah 文章的实时演示是这个:
这是一个 Felix Hornoiu 提供的另一个变形示例:
您甚至可以变形用作剪切蒙版的路径的值!Heather Buchel 提供了这样一个示例:
<animateMotion>
元素
沿任意路径动画化:<animateMotion>
元素是我最喜欢的 SMIL 动画元素。您可以使用它来沿路径移动元素。您可以使用两种方式之一(我们将在下面介绍)来指定运动路径,然后设置元素使其沿着该路径移动。
<animateMotion>
元素接受前面提到的相同属性,另外还有三个属性:keyPoints
、rotate
和 path
。此外,在calcMode
属性方面,还有一个区别,即对于<animateMotion>
,默认值为paced
,而不是linear
。
path
属性指定运动路径
使用path
属性用于指定运动路径。它以与path
元素上的d
属性相同的格式表示,并以相同的方式解释。运动路径动画的效果是在被引用对象的当前变换矩阵上添加一个补充变换矩阵,这会导致沿当前用户坐标系的 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
属性中的坐标是相对于元素的当前位置的!
上面代码的结果是:
如果您要从(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 个单位,然后开始相对于新位置沿着路径进行运动。因此,在为动画准备运动路径时,请务必牢记这一点。
如果使用,属性from
、by
、to
和values
将指定当前画布上的一个形状,该形状表示运动路径。
<mpath>
元素指定运动路径
使用您还可以使用另一种方式来指定运动路径。您可以使用<mpath>
元素引用外部路径,而不是使用相对的path
属性。<mpath>
(<animateMotion>
元素的子元素)将使用xlink:href
属性引用外部路径。
<animatemotion xlink:href="#circle" dur="1s" begin="click" fill="freeze">
<mpath xlink:href="#motionPath"></mpath>
</animatemotion>
运动路径可以在文档中的任何位置定义;它甚至可以完全在<animateMotion>
元素中定义,而根本不渲染在画布上。在下面的示例中,路径被渲染了,因为在大多数情况下,您可能希望显示元素沿着其移动的路径。
请注意,根据规范:
形状的各种 (x, y) 点为被引用对象在 CTM 上提供了补充变换矩阵,该矩阵会导致沿当前用户坐标系的 x 轴和 y 轴进行平移,平移量由随时间计算的形状的 (x, y) 值决定。因此,被引用对象会随着时间的推移而平移,平移量为运动路径相对于当前用户坐标系原点的偏移量。补充变换将应用于目标元素的
transform
属性或由于目标元素上的animateTransform
元素而对该属性的任何动画所导致的任何变换之上。
同样,圆形的位置将被路径数据中的坐标“乘以”或“变换”。
在下面的示例中,我们有一个位于画布中间的路径。圆形位于路径的起点。但是,当应用运动路径时,圆形不会从其当前位置开始运动。查看演示以获得更好的解释。单击圆形以对其进行动画化。
请注意,圆形确实沿着路径的相同形状移动,但位于不同的位置?这是因为圆形的位置被路径数据的值变换了。
解决此问题的一种方法是,让圆形从(0, 0)
开始定位,这样,当使用路径数据对其进行变换时,它将按预期开始并继续进行。
另一种方法是应用一个变换来“重置”圆形的坐标,使其在应用路径之前计算为零。
下面是上面演示的修改版本,它使用封闭路径并无限期地重复运动动画。
<animateMotion>
的覆盖规则
由于<animateMotion>
有多种方式可以做同样的事情,因此只有拥有覆盖规则来指定哪些值会覆盖其他值才合乎逻辑。
<animateMotion>
的覆盖规则如下:
- 关于运动路径的定义,
<mpath>
元素会覆盖path
属性,path
属性会覆盖values
,values
会覆盖from
、by
和to
。 - 关于确定与
keyTimes
属性相对应的点,keyPoints
属性会覆盖path
,而path
会覆盖values
,而values
又会覆盖from
、by
和to
。
rotate
设置元素沿运动路径的方向
使用在我们之前的示例中,我们沿着路径动画的元素碰巧是一个圆形。但是,如果我们动画的是具有特定方向的元素,比如一辆汽车图标呢?以下示例中的汽车图标由Freepik设计。
在本例中,我用一个 ID 为“car” 的组替换了圆形,该组包含构成该组的元素。然后,为了避免上述沿着路径运动的问题,我将一个变换应用于汽车,通过特定的量来平移它,以便初始位置最终位于 (0, 0)。变换中的值实际上是汽车第一条路径开始绘制的点的坐标(在移动命令 M 之后)。
然后,汽车开始沿着运动路径移动。但是... 这就是运动的样子
汽车的方向是固定的,不会改变以匹配运动路径的方向。为了改变这一点,我们将使用rotate
属性。
rotate
属性可以取三个值之一
auto
:表示随着时间的推移,对象旋转运动路径方向(即方向切线向量)的角度。auto-reverse
:表示随着时间的推移,对象旋转运动路径方向(即方向切线向量)的角度加上 180 度。- 数字:表示目标元素应用了恒定的旋转变换,旋转角度为指定的度数。
为了修正上述示例中汽车的方向,我们将从将旋转值设置为auto
开始。最终我们将得到以下结果
如果你想让汽车在路径外移动,auto-reverse
值可以解决这个问题。
这样看起来好多了,但是我们仍然存在一个问题:汽车看起来像是沿着路径向后移动!为了改变这一点,我们需要沿着 y 轴翻转汽车。这可以通过沿着该轴以“ -1 ”的因子缩放它来完成。因此,如果我们将变换应用于具有car
ID 的g
,汽车将如预期的那样向前移动。缩放变换将与我们之前应用的平移变换相连。
最终的演示如下
keyPoints
控制动画沿着运动路径的距离
使用keyPoints
属性提供了为每个指定的keyTimes
值指定沿着运动路径的进度能力。如果指定了,keyPoints
会导致keyTimes
应用于keyPoints
中的值,而不是values
属性数组中指定的点或path
属性上的点。
keyPoints
采用以分号分隔的 0 到 1 之间的浮点值列表,并指示对象在由对应keyTimes
值指定的时间点沿着运动路径移动多远。距离计算由浏览器的算法确定。列表中的每个进度值对应于keyTimes
属性列表中的一个值。如果指定了keyPoints
列表,则keyPoints
列表中的值必须与keyTimes
列表中的值一样多。
这里需要注意的一点是,为了使keyPoints
生效,需要将calcMode
的值设置为linear
。它看起来也应该在逻辑上与步进动画一起使用,如果你的关键点来回移动,但它没有。
以下是 Amelia Bellamy-Royds(你应该去看看她的CodePen 个人资料)的示例,该示例使用keyPoints
来模拟从预定义的偏移量开始沿着路径进行运动的行为,因为我们目前在 SMIL 中默认情况下没有这种能力。
沿着任意路径移动文本
沿着任意路径移动文本与沿着路径移动其他 SVG 元素不同。要动画化文本,你将不得不使用textPath
元素,而不是animateMotion
元素。
首先,让我们从将文本沿着路径定位开始。这可以通过在textPath
元素内部嵌套一个text
元素来完成。将沿着路径定位的文本将在textPath
元素内部定义,而不是作为textPath
元素的子元素。
然后,textPath
将引用我们要使用的实际路径,就像我们在前面的示例中所做的那样。引用的路径也可以在画布上渲染,也可以在
元素中定义。检查以下演示中的代码。
要沿着该路径动画化文本,我们将使用animate
元素来动画化startOffset
属性。
startOffset
表示文本在路径上的偏移量。0% 是路径的起点;100% 表示路径的终点。因此,例如,如果偏移量设置为 50%,则文本将从路径的中点开始。我想你可以看到我们接下来要做什么。
通过动画化startOffset
,我们将创建文本沿着路径移动的效果。检查以下演示中的代码。
<animatetransform></animatetransform>
元素
动画化变换:animateTransform
元素动画化目标元素上的变换属性,从而允许动画控制平移、缩放、旋转和/或倾斜。它接受与animate
元素相同的属性,再加上一个额外的属性:type
。
type
属性用于指定正在动画化的变换的类型。它可以取五个值之一:translate
、scale
、rotate
、skewX
和skewY
。
from
、by
和to
属性取一个值,该值使用与给定变换类型相同的语法表示
- 对于
type="translate"
,每个单独的值表示为<tx> [,<ty>]</ty></tx>
。 - 对于
type="scale"
,每个单独的值表示为<sx> [,<sy>]</sy></sx>
。 - 对于
type="rotate"
,每个单独的值表示为<rotate-angle> [<cx> <cy>]</cy></cx></rotate-angle>
。 - 对于
type="skewX"
和type="skewY"
,每个单独的值表示为<skew-angle></skew-angle>
。
如果你不熟悉 SVG transform
属性函数的语法,为了本文的简洁性和由于语法细节以及它如何工作超出了本文的范围,我建议你在继续本指南之前阅读我之前写过的关于此主题的文章:“Understanding SVG Coordinate Systems and Transformations (Part 2): The transform
Attribute”。
回到之前的演示,我们在其中使用<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>
from
和to
属性指定旋转的角度(起点和终点)以及旋转的中心。当然,在两者中,旋转的中心都保持不变。如果你没有指定中心,它将是 SVG 画布的左上角。上述代码的实时演示如下
这是一个由 Gabriel 制作的另一个有趣的示例,其中只有一个animateTransform
动画化单个变换很简单,但是,当包含多个变换时,事情会变得非常混乱和复杂,尤其是因为一个animateTransform
可以覆盖另一个,因此你可能最终得到完全相反的结果,而不是添加和链接效果。此外还有 SVG 坐标系和变换的实际工作方式(参考之前提到的关于该主题的文章)。示例很多,超出了本文的范围。对于变换 SVG,我建议使用 CSS 变换。实现正在努力使后者与 SVG 完美配合,因此你可能根本不需要使用 SMIL 来动画化 SVG 中的变换。
<set></set>
元素
set
元素提供了一种简单的方法来设置指定持续时间的属性的值。它支持所有属性类型,包括那些无法合理插值的属性类型,例如字符串和布尔值。set
元素是非累加的。不允许使用累加和累积属性,如果指定它们将被忽略。
由于set
用于在特定时间设置元素到一个特定值,因此它不接受之前提到的动画元素的所有属性。例如,它没有from
或by
属性,因为更改的值不会在一段时间内逐步更改。
对于set
,您可以指定目标元素、属性名称和类型、to
值,并使用以下参数控制动画时间:begin
、dur
、end
、min
、max
、restart
、repeatCount
、repeatDur
和fill
。
以下示例演示了在单击旋转矩形时将其颜色设置为蓝色。颜色将保持蓝色 3 秒,然后恢复为原始颜色。每次单击矩形时,都会触发set
动画,并且颜色会更改 3 秒。
可以动画化的元素、属性和特性
并非所有 SVG 属性都可以动画化,并且并非所有可以动画化的属性都可以使用所有动画元素进行动画化。有关所有可动画化属性的完整列表以及显示哪些元素可以动画化哪些属性的表格,请参阅SVG 动画规范的这一部分。
最后的话
SMIL 潜力巨大,我只是触及了表面,只涉及了它们在 SVG 中的工作原理的基础知识和技术细节。可以创建许多非常令人印象深刻的效果,尤其是涉及变形和变换形状的效果。天马行空吧!别忘了与社区分享你的作品;我们很想看看你做了什么。感谢您的阅读!
本文已根据评论中的讨论进行了更新。感谢你的意见,Amelia。=)
哇,我不知道 SVG 内置了路径插值!这太令人兴奋了。感谢您提供这些精彩的信息汇编,Sara!
很棒的文章!我<3 SVG
谢谢!
也许 Flash 终于找到了自己的用途:SVG/SMIL 动画生成器 :-)
你的意思是,用 Flash 可以生成 SVG 动画吗?
哇!
SVG 对我来说一直都是“嗯,你可以用它创建一些形状...” 探索了 Jakob Jenkov 的 SVG 教程(http://tutorials.jenkov.com/svg/index.html)后,情况肯定发生了变化。
这篇文章也加入了我的阅读清单!
这太酷了!
我喜欢“Gabriel 的单一 animateTransform”
哇,很棒的人工 SVG,不错。如何学习 SVG,哪个网站?
我记得我的那些“Macromedia Flash”时代:p
很棒的概述。
一些更正,希望可以将其整合,使其成为持久的参考
你在这里混淆了两个不同的概念(尽管规范并没有好到哪里去)。CSS 不是 XML 命名空间,你不能使用
attributeName
来指定它是你要动画化的 CSS 属性(尽管如果你不指定attributeType
,这是默认行为)。规范中的“XML 命名空间”指的是例如xlink:href
中的 xlink。我不确定离散模式有什么特别之处,尽管它确实让差异更加明显。更一般地说,
by
的作用与同时指定from
和to
相同。只有在单独指定to
时,并且仅在进行加性动画(累积重复动画或同一属性的多个加性动画)时,情况才有所不同;前两种形式被解释为累积偏移量,但to
被解释为最终值,与起始值无关。JavaScript 似乎过于复杂。使用 values 语法很容易实现:
values="0;100;0"
会在 0 和 100 之间创建交替动画。请记住,dur
现在是动画完整循环(来回)的持续时间。我仍然希望有一种更简单的方法来交替重复(如果你的值是复杂的路径数据命令,重复所有内容会很麻烦),但可以实现。它有点小问题,但它有效。这是 David Eisenberg 为《SVG Essentials,第二版》准备的示例。
值得一提的是,您可以使用
values
而不使用keyTimes
。值会自动均匀地分布在整个时间内(对于除paced
以外的所有计算模式)。paced
忽略keyTimes
,但不会覆盖values
。它计算出后续值之间的距离,并相应地划分时间。如果你的所有值都是线性顺序的,你就不会注意到区别。但如果它们来回移动,或者如果它们是颜色(被视为三维向量值),你肯定会看到中间值。http://codepen.io/AmeliaBR/pen/EzAju?editors=100
这不是真的;任何 end 触发器都会停止动画,无论它是如何开始的。例如,如果在 end 触发器列表中包含
click
,则单击会停止当前动画(但不会阻止将来的动画)。我倾向于从相反的角度考虑这一点——路径是固定的,但圆形坐标系的 (0, 0) 点会沿着它移动,而不一定是圆形本身。但无论哪种方式都可以。
更准确地说,它们分别指定
x,y
格式的坐标(或values
的半冒号分隔的坐标对列表)。运动是沿着从一个坐标到下一个坐标的直线进行的。keyPoints
可以正常工作,但你需要设置calcMode="linear"
。如果你的关键点来回移动,它在逻辑上应该与 paced 动画一起工作,但它没有。例如,请参见http://codepen.io/AmeliaBR/pen/ewvrC?editors=100
这是一个非常巧妙的示例,它使用两个不同的路径元素(一个在前,一个在后),模拟了动画 z-index(SVG 目前没有!)。
当然,你发现了一些我没有注意到的规范内容。我没有注意到你应该能够使用时钟值作为 begin/end 时间偏移量,所以我不担心它不起作用(它们作为独立值可以正常工作)。我还忽略了与事件驱动的 begin/end 值关联的对象 ID 是可选的。虽然我知道
min
和max
,但我认为我从未使用过它们。我没有意识到,在具有路径但没有keyPoints
的“ 上使用keyTimes
(或calcMode="linear"
,一般来说)会使用路径的顶点而不是关键点。@Amelia
感谢您的评论。
我不记得自己在文章中说过这两句话中的任何一句。:o 我从来没有说过 CSS 是 XML 命名空间,而且我说的是您使用
attributeType
来指定命名空间,而不是attributeName
。赞同。这可以添加到文章中,以显示
by
的另一种用例。尽管正如我在文章中提到的,by
的效果在使用离散步调时更明显,因此文章中并没有技术上的错误。我并不反对,但这并不能改变 SMIL 在这方面存在缺陷的事实。也就是说,将这篇文章添加到文章中会很好。谢谢。
太奇怪了。我刚尝试了一个使用
repeat()
的演示,我之前在写文章时尝试过,它之前无法工作,但现在可以了!我很快就会添加一个示例(并链接到您显示的示例)。也将添加。
这是我这边的一个错误,抱歉。规范说明它只覆盖
keyTimes
。我的错。:) 也将修复。这是真的。是的,
end
触发器将停止动画,这也适用于其他用例,例如,当您有多个重复时,例如,end
值也可以覆盖其他活动持续时间,但我没有为了简洁起见而深入到具体的用例和示例中。文章中的句子在技术上仍然是正确的。没错,我错过了它与
calcMode="linear"
一起工作的事实。感谢您提醒。FWIW,它不应该是一个如何操作的演示;只是一个展示可能性的演示。
还要注意,SVG 确实有一个
z-index
属性,但它还没有在任何浏览器中实现。它是 SVG2 的一部分。感谢您提供的所有意见。将根据需要更新文章,包括相关说明。
干杯!
谢谢 Sara,
再次强调,我并不是为了挑剔而挑剔,而是因为我认为这是一份很棒的资源,应该尽可能清晰。
关于对
begin
和end
使用多个值,我想说的是,它们不是像keyTimes
值与keyPoints
或values
相匹配的那样,匹配的开始时间和结束时间列表。您的示例很好地将它们匹配在一起,但它们不必匹配。您可以颠倒其中一个列表中的时间顺序,动画仍然会以相同的方式运行。或者您可以有许多开始时间,但只有一个覆盖的结束时间或事件。关于 Gabriel 的示例,当我称它为狡猾时,我并不是在挑剔。如果互联网评论可以包含语气,这句话就会以“哇,太棒了!”的方式说出来。也就是说,我必须非常仔细地查看代码才能弄清楚它是如何工作的。我也在热切地等待 SVG2 中的 z-index。
@Chris Coyier 我发布了一篇非常长的评论,总结了文章中的一些错误/模棱两可的陈述,但它没有显示出来。如果只是因为它太长而被垃圾邮件隔离,没问题。如果没有,请给我发个便条,我会重新发布或直接发送给你。
@Amelia BR
感谢您的评论(这个评论和没有发布的评论)。文章中缺少一部分,我写文章时原始文档没有保存那一部分——出于某种原因——所以这可能是造成模棱两可的原因?不确定。我会添加那一部分,请随时将清单直接发送给我,@ [email protected]
对于实验表,这令人难以置信地有见地。
哇,这是一篇史诗般的文章,感谢所有演示和解释,做得非常好。
干杯
哇……很棒的文章……我喜欢 SVG……
但这里有一个问题:如何处理碰撞?
即,如果我使用“沿路径移动”示例作为基础开发一个游戏,我如何知道汽车何时会遇到障碍物?
在 Javascript 中,您始终可以访问元素对象的任何 SVG 属性的当前动画值,在相关属性的
animVal
子属性中。例如,在这个笔中,我为圆圈的
cx
制作动画,并访问当前值,如{element}.cx.animVal.value
(以 SVG 用户单位,即像素为单位)或{element}.cx.animVal.valueAsString
(以原始单位为单位,在本例中为百分比)。http://codepen.io/AmeliaBR/pen/awvdB
对于沿路径的运动,它会变得更复杂一些。动画是使用 SVG
transform
属性应用的。相应的属性是{element}.transform
,其类型为 SVGAnimatedTransformList。您可以探索该链接中定义的 DOM 对象、方法和属性,以发现使用 SVG DOM 处理变换的可能性。哇……
@AmeliaBR:非常感谢您在解释方面的努力,以及出色的、易于理解的示例……
我将在本周末尝试 DOM 方法……
@Amelia
不用担心,感谢您提出的建设性意见和笔记。我很乐意随时添加您的笔记并更正我的错误。:)
干杯!
(附注:是的,书面的“语气”可能会被误解,这就是为什么我倾向于在我的评论中使用很多笑脸。)
((附注#2:您的 SVG 知识非常棒!我曾经分享过您关于使用 CSS 变量来设置“元素内容的样式的文章。好东西! :))
路径怎么样?!
我如何在 SVG 中绘制路径?!.. 从 A ……………………… 到 B )
哇!刚接触 SVG,刚找到这个。值得……
哇,这些简短的示例令人兴奋。我期待尽快阅读整篇文章。太好了!
非常有趣的文章。但我喜欢用 canvas 和 JS 做这样的事情。
首先,非常感谢 Sara,您为 SVG 动画提供了这份真正史诗般的指南!
不过,我想更多地阐明
repeat(n)
函数的行为。我注意到,在您添加到文章中的 David 的示例中,它可以工作(在 Chrome 38+ 中),但不幸的是,我在文章中的实时示例中无法让它工作。所以我玩了一下,发现只有在以下两个条件都为真时,它才会起作用。1. 正在引用的动画的
ID
不包含连字符 (-
);2. 正在引用的动画的
repeat(n)
中的 n 小于正在引用的动画的repeatCount
。如果其中任何一个是假的,
repeat(n)
就会停止工作。第一个对我来说尤其奇怪,因为
-
字符对 XML ID 有效,据我所知。begin
和end
属性的解析规则是否对它施加了一些额外的约束,可能是因为-
也可以被解释为时钟值的减号?还是仅仅是 Chrome 的 bug?对于后一个条件,我的直观解释是,
repeat(n)
事件不会在整个动画结束之后触发(即,如果动画有 2 次重复,在第二次重复之后,end
事件会先触发,并阻止repeat(2)
事件触发)。不幸的是,我在规范中找不到我假设的证明或反驳。我的猜测错了吗?@SelenIT
感谢您的意见!
我确实遇到了第一个带连字符的问题。我不记得我是否问过别人这个问题,也不记得是否提交过 bug 报告,因为我不确定它到底是否是 bug;感谢您在此指出这一点。
第二点确实很有趣,而且考虑到重复次数应该小于
repeatCount
,所以很有道理——为什么在动画只重复两次的情况下引用第三次重复?而且用你的方式说也有道理:我认为end
事件也可能是原因。我的想法是,每次重复都“注册”为一个
begin
。我在这里说不通,但我无法想到另一种表达方式。再次感谢您宝贵的意见。:)
干杯!
Sara 的教程很棒!30分钟内,我就学会了如何创建自己的动画,并准备在网站上使用它。如果你还在,我想问一个可能很简单的问题。
我有一个形状渐变动画,它使用 animate 属性在浏览器中来回播放。
有没有办法用纯 CSS 或 Javascript 来实现?我想将其用作状态改变动画。我有一个形状作为加载图像,然后我希望它在准备就绪时渐变为播放按钮。非常感谢你的指导!
Sara,感谢你提供了如此棒的 SMIL 教程,还有很棒的示例!不过我得说两句。当我构建 SVG Circus 的初始版本时,我在使用
calcMode=spline
和keyTimes/keySplines
数组值的“路径运动”动画中发现了一些跨浏览器问题。这使得很难制作一个非线性循环动画,例如,将缓动应用于五个循环。因此我决定停止使用 SMIL,用 Javascript(基于 RAF)编写了一个自定义 SVG 动画引擎,并将 SVG Circus 移植到它上面。我昨天开源了它,所以欢迎你尝试使用它 :)你能用更多例子再解释一下
keyTimes 和 keySplines,以及它们之间的关系