SVG的本机动画规范SMIL已受到高度重视,因为它为执行SVG动画渲染提供了许多技巧。 不幸的是,在WebKit中对SMIL的支持正在减弱,并且对于Microsoft的IE或Edge浏览器从未(也永远不会)存在。 没有恐惧! 我们已经覆盖了您。 本文探讨了某些特定于SMIL的功能,并深入研究了替代方法,以期获得更长的支持,从而达到相同的效果。
SMIL功能:沿路径运动
SMIL为SVG中逼真的运动提供的最引人注目的东西之一是沿着路径的运动。 现实生活中很少有事物沿直线运动,因此沿着路径运动可以使我们模仿日常生活中看到的事物。 以前,您需要使用path将SVG路径传递到animateMotion中,并定义路径数据。 通过使用xlink:href =”#thingtoanimate”指定要选择动画的元素。
<animateMotion
xlink:href="#lil-guy"
dur="3s"
repeatCount="indefinite"
fill="freeze"
path="M 25,50 C 37.5,25 37.5,25 50,0 75,50 75,50 100,100 50,100 50,100 0,100 12.5,75 12.5,75 25,50 Z" />
见笔SMIL运动路径萨拉Drasner( @sdras )上CodePen 。
另类:CSS
对我们来说幸运的是,沿着路径模块的运动现在正在移入CSS 。 到目前为止,这种支持是微不足道的( 仅适用于Chrome,Opera和Android ):,但Sara Soueidan提议在Edge中采用,并且到目前为止,它已经得到了强有力的支持,在本文发表时,它获得了大约420票以上的支持。 请添加您的声音,以确保将提供此功能。 Firefox的投票在这里 。
据我所知,在Safari中投票寻求支持会更加个人化。 我注册以填写错误报告,并请求CSS中的运动路径模块作为一项功能。
为了在CSS中沿路径使用运动,您可以将路径数据传递给offset-path属性,如下所示:
.move-me {
offset-path: path('M3.9,74.8c0,0,0-106.4,75.5-42.6S271.8,184,252.9,106.9s-47.4-130.9-58.2-92s59.8,111.2-32.9,126.1 S5.9,138.6,3.9,74.8z');
}
请参阅CodePen上CSS-Tricks( @ css-tricks )CSS中的“笔与运动路径模块一起 玩” 。
我从在Illustrator中制作的SVG获取路径数据,然后在SVGOMG中进行优化。
在这种情况下,我要确保它遵循从起点一直到路径终点的路径,并且您可以看到它是一条闭合路径,并由结尾处的小z表示。 这意味着这条路是一条环形路,所以这个怪异的小动物将最终到达他开始的那个地方。 我在关键帧值中设置这些参数,仅指定100%的值,因为默认值设置为零:
@keyframes motionpathguy {
100% {
motion-offset: 100%;
}
}
然后在元素上调用动画:
.move-me {
animation: motionpathguy 10s linear infinite both;
}
另一种选择:GreenSock的运动轨迹
如果您需要当前最广泛的支持和最灵活的实现,则应使用GreenSock。 GSAP的Bezier-Plugin(默认情况下与TweenMax打包在一起)为非SVG元素提供了对IE7的支持,对SVG提供了对IE9的支持(可用的最广泛的SVG动画支持)。 它就像手机上的大爆炸一样。 我之前在David Walsh博客上写过有关此内容的文章,但这里有一个简短的概述,以及此后出现的一些其他新功能:
最初,您将传递一个值数组:
bezier: {
type: "soft",
values:[{x:10, y:30}, {x:-30, y:20}, {x:-40, y:10}, {x:30, y:20}, {x:10, y:30}],
autoRotate: true
}
但是正如您在此处看到的,您也可以选择自动旋转(或不自动旋转),就像SMIL的旋转一样。 如果您缺少SMIL为旋转的初始位置或旋转角度指定自动反转或auto:n
参数的功能,则GSAP允许您使用rotation:90更改旋转角度的数量,或者在需要时使用更有限的控制:
autorotate: [
first position property, like "x",
second position property, like "y",
rotation property, typically "rotation" but can be “rotationY”,
integer for radians/degrees the rotation starts from like 10,
boolean for radians or degrees- radians is true
]
在SMIL中,您可以变换路径或组以在对象移动时更改其方向。 在GSAP中,您可以使用autoRotate: false
轻松实现此autoRotate: false
,并使用set初始化旋转。 您也可以像使用SMIL一样转换SVG属性本身上的元素,尽管这样做不太优雅,并且在工作时更难以跟踪。
TweenMax.set("#foo" {
rotation: 90 // or whatever number
});
您还可以将类型设置为thru
, soft
, quadratic
或cubic
。 有关这些工具的更多文档, 请参见GreenSock API文档 。 的nice值thru
是能够影响的元件上弯曲度的量。 如果您将这些点视为要往返的坐标,则曲线将控制这些点之间的路径选择的直接程度。 0将是一条直接路径,1会稍微宽松一些,2会形成一条优美的曲线,3或更高的值会自动缠绕。
见笔演示的弯曲度在使用GreenSock贝塞尔萨拉Drasner( @sdras )上CodePen 。
最近,GreenSock还公开了传递路径数据(如CSS和SMIL模块)的功能,就像使用本地SMIL一样。 这是对他们的MorphSVG插件的扩展,因此您可以添加该插件,并按以下方式使用它:
TweenMax.to("#lil-guy", 3, {
bezier: {
MorphSVGPlugin.pathDataToBezier("#path", {align: "#lil-guy" }),
type: "cubic"
},
ease: Linear.easeNone,
repeat: -1
});
<path id="path" d="M 25,50 C 37.5,25 37.5,25 50,0 75,50 75,50 100,100 50,100 50,100 0,100 12.5,75 12.5,75 25,50 Z" fill="none" />
见笔SMIL运动路径萨拉Drasner( @sdras )上CodePen 。
默认设置是将我要设置动画的组的左上角(在本例中为#lil-guy
)设置为路径轨迹。 这将使其在视觉上看起来未对齐。 所以我将#lil-guy
设置为使用中心点,而不是使用TweenLite.set
:
TweenLite.set("#lil-guy", {xPercent:-50, yPercent:-50});
您还可以通过将对象作为第二个参数传递给该方法来偏移这些路径,并在pathDataToBezier
定义offsetX
和offsetY
-请注意,您可能需要扩展viewBox
以使要动画的组或属性不会被裁剪出来。 设置书呆子格式:出于可读性原因,我将对象放在新行上。
// offset the path coordinates by 125px on the x-axis, and 50px on the y-axis:
TweenMax.to("#lil-guy", 3, {
bezier: {
values: MorphSVGPlugin.pathDataToBezier("#path", {
offsetX: 125,
offsetY: 50,
align: "#lil-guy"
}),
type: "cubic"
},
ease: Linear.easeNone,
repeat: -1
});
见笔SMIL运动路径萨拉Drasner( @sdras )上CodePen 。
您甚至可以为此定位定义矩阵坐标。
// scale the path coordinates up by 1.25
// and shift it over 120px on the x-axis
// and up 30px on the y-axis:
TweenMax.to("#lil-guy", 3, {
bezier: {
values: MorphSVGPlugin.pathDataToBezier("#path", {
matrix:[1.5,0,0,1.5,120,-30],
align:"lil-guy"}),
type: "cubic"
},
ease: Linear.easeNone,
repeat: -1
});
见笔SMIL运动路径萨拉Drasner( @sdras )上CodePen 。
另一个选择是将align
成员设置为"relative"
。 通过保持每个坐标相对于x:0
, y:0
的位置,它可以防止跳跃。 在先前的笔中,我使用align
将机芯与#lil-guy
组本身配对。
有关GreenSocks的Bezier插件API中的这一新功能(如本篇文章当天的新内容一样)的更多信息,请查看其文档以及该精彩的解释性视频 。
SMIL功能:形状变形
以前,您可以将路径数据作为值传递到animate属性中,以使形状变形。 诺亚·布朗(Noah Blon)有一个很好的例子:
见笔Sitepoint挑战SVG和SMIL#1挪亚伦( @noahblon )上CodePen 。
替代方法:Snap.svg或SVG Morpheus
一些库提供了变形路径或形状值,例如Snap.svg和SVG Morpheus ,但要注意的是(即使在SMIL中)形状必须具有相同数量的点,或者变形看起来很糟糕或完全失败。 这在预处理方面令人失望,因为这意味着您必须很好地跟踪所做的工作,或者与设计人员进行良好的协作以确保获得此(有时是任意的)中点数据。 这些额外的点也不必要地膨胀了您的代码。
另一种选择:GreenSock MorphSVG
我强烈建议您使用GSAP的MorphSVG插件,因为它可以用不同数量的点精美地变形形状和路径。 请参阅此网站徽标上的切换按钮,以获取有关实际运行中的变体的演示。 这是另一个例子:
见笔可互换行家萨拉Drasner( @sdras )上CodePen 。
因为MorphSVG插件会补间路径数据,所以如果需要转换形状,则可以使用其convertToPath
选项:
MorphSVGPlugin.convertToPath("ellipse");
// or circle, rect, etc
这使我们能够进行非常复杂的形状补间,并且可以改变网络上的所有运动。
该插件提供了一些额外的功能,真正使它脱离了公园。 第一个是实用程序插件findShapeIndex
。 假设您对形状的变形方式不满意(尽管9乘以10,自动预设就可以了)。 您加载了插件(不用担心,因为在生产中不需要它,所以不会增加额外的重量),并且传入两个值:第一个形状的补间ID和第二个形状的ID。 将会弹出一个GUI,您可以在其中切换值,并且它还将自动使用repeat: -1
,以便在形状之间不断循环。
findShapeIndex("#hex", "#star");
// you can comment out above line to automatically disable findShapeIndex() UI
见笔SMIL运动路径萨拉Drasner( @sdras )上CodePen 。
一旦你的附加价值,你会通过shapeIndex
的内morphSVG
对象:
TweenLite.to("#hex", 1, {morphSVG: { shape: "#star", shapeIndex: 1 }});
这些额外功能中的第二个是插件解析切出路径的能力,这是其他库所没有的。 最后,您还可以重用第一个起始ID(而不是必须存储该路径数据以供重用)。 值得一提的是,当插件首次发布时,这些功能尚不可用,但是GreenSock意识到需要支持,因此将其包含在内。
既然我们不局限于特定数量的点,我们就扩大了产生不同种类效果的可能性。 下面,我抽了一些烟:
在 CodePen上查看 Sarah Drasner( @sdras ) 撰写的《 有烟的笔》 。
SMIL功能:DOM事件
诸如悬停和单击之类的东西很好地融入了SMIL中。 为了开始工作,可以指定begin="click"
或begin="hover"
。
<animate
xlink:href="#rectblue"
attributeName="x"
from="0"
to="300"
dur="1s"
begin="click"
values="20; 50"
keyTimes="0; 1"
fill="freeze" />
见笔SMIL运动路径萨拉Drasner( @sdras )上CodePen 。
另一种选择:JavaScript
有一些本地DOM事件,例如onmouseenter
和onmouseleave
用于悬停并click
(例如,单击)。 您可以使用它们进行更改以触发基于JavaScript的动画。
另一种选择:JavaScript + CSS
您可以使用JavaScript更改类名或直接更改CSS样式。 这是可能的:更改animation-play-state
以从事件触发器启动动画。
.st0 {
animation: moveAcross 1s linear both;
animation-play-state: paused;
}
@keyframes moveAcross {
to {
transform: translateX(100px);
}
}
document.getElementById("rectblue").addEventListener("click", function() {
event.target.style.animationPlayState = "running";
});
或在jQuery中:
$(".st0").on("click", function() {
$(this).css("animation-play-state", "running");
});
见笔SMIL运动路径萨拉Drasner( @sdras )上CodePen 。
此实现不会像SMIL示例中那样立即将动画重新设置为开始。 如果您想做到这一点,那么上一篇有关CSS-Tricks的文章详细介绍了一些不错的方法。
另一种选择:Greensock
在GSAP中,重新启动更为简单。 我们可以将动画添加到时间轴,将其设置为暂停,然后单击重新启动它。 此实现与您对SMIL的期望有点接近,因为我们不必做任何麻烦的事情,例如克隆/重新插入DOM节点或更改元素上设置的任何属性。
// instantiate a TimelineLite
var tl = new TimelineLite();
// add a tween to the timeline
tl.to(foo, 0.5, { left: 100 });
$(".st0").on("click", function() {
tl.restart();
});
SMIL功能:在“ Y”完成后运行“ X”
SMIL还允许进行更复杂的定时事件,例如begin="circ-anim.begin + 1s"
。 当链接动画时,这特别有用。
另类:CSS
在CSS中,我们可以通过在第二个值上设置延迟来链接动画:
.foo {
animation: foo-move 2s ease both;
}
.bar {
animation: bar-move 4s 2s ease both;
/* the 2 second value corresponds with the length of the iteration of the first. */
}
这种工作方式有点令人讨厌,因为您必须确保记住要更改第一个间隔以及延迟。
另一种选择:CSS预处理
如果我们在(例如)Sass中使用变量,则维护和管理这些间隔会变得容易一些:
$secs: 2s;
.foo {
animation: foo-move $secs ease both;
}
.bar {
animation: bar-move 4s $secs ease both;
}
现在我们知道,如果我们更新一个值,它们将保持同步。
但是,如果我们想在动画完成时,总是发现,JavaScript的提供了这跟一些不错的本地功能animationEnd
:
$("#rectblue").on("animationend", function() {
$(this).closest("svg").find("#rectblue2").css("animation-play-state", "running");
});
#rectblue2 {
animation: moveAcross 2s 1s ease both;
animation-play-state: paused;
}
您可能需要单击重播才能看到以下动画:
见笔SMIL运动路径萨拉Drasner( @sdras )上CodePen 。
对于延迟,我们可以将其烘焙到元素本身CSS的animation-delay
属性中,就像上面一样,或者我们可以使用setTimeout
来表示:
setTimeout(function timeoutHandler() {
// animation goes here, with any language
}, 1000); // wait for a second
另一种选择:Greensock
我最喜欢的选项是添加动画时间轴的有限控制。 我们可以为此使用GreenSock的TimelineLite,可以用几种不同的方式表示:
简单的时间轴:
// instantiate a TimelineLite
var tl = new TimelineLite();
// add a tween at the beginning of the timeline
tl.to(foo, 0.5, { left: 100 });
// use the position parameter "+=1" to schedule next tween 1 second after the previous tweens end
tl.to(foo, 0.5, { left: 200 }, "+=1");
具有相对标签的时间轴:
// add a label 0.5 seconds later to mark the placement of the next tween
tl.add("myRelativeLabel")
// use the label to specify an animation a second after the
tl.to(foo, 0.5, { scale: 0 }, "myRelativeLabel+=1");
// or to use to this label for things like interaction
tl.play("myRelativeLabel");
我更喜欢相对标签,因为您可以选择一个时间点,很多东西会触发或延迟,即使该时间点调整了,您也不必像在CSS中那样进行任何重新计算。 。
时间轴的好处是,您可以在一个位置上很好地控制许多不同的对象,并且可以提供诸如repeatDelay
(多次重复之间的延迟)之类的功能。
SMIL提供了repeatDur
,如果您不希望将其作为默认值(例如repeatDur="01:30"
,则可以让您说出重复迭代的时间。 在GreenSock中,您可以加快或减慢时间轴,从而通过timeScale(n)
调整重复长度,或将repeat: -1
次数设置为repeat: -1
,否则将设置repeatDur="indefinite"
。
方便的Dandy更换参考表
既然我们已经深入研究了一些最有用的SMIL特定功能,那么值得注意的是,您可能已经了解了SMIL功能的许多替代品。 我们制作了下表,以便快速参考这些更简单的实现。
SMIL代码 | 代号 | 替代技术 |
关键时间 | @keyframes | CSS |
keySplines | 三次贝塞尔曲线 | CSS |
重新开始 | 重新开始(); | GSAP |
calcMode =“离散” | 脚步() | CSS |
去掉 | 杀(); 明确(); clearProps:“全部” | GSAP |
冻结 | 动画播放状态:已暂停 | CSS |
冻结 | 暂停(); | GSAP |
填 | 动画填充模式 | CSS |
repeatCount =“不确定” | animation-iteration-count:无限; | CSS |
repeatCount =“不确定” | 重复:-1 | GSAP |
whenNotActive | 在JS中检测动画播放状态 | CSS,香草JavaScript或jQuery |
animateMotion路径 | 运动路径 | CSS |
animateMotion路径 | 贝塞尔 | GSAP |
设置动画值(路径变形) | 变形SVG | GSAP |
开始=“悬停” | mouseover,mouseenter / 鼠标移开,鼠标离开 | jQuery,香草JS |
begin =“点击” | 点击 | jQuery,香草JS |
begin =” circ-anim.begin + 1s” | 动画延迟:$ vars; | 萨斯 |
begin =” circ-anim.begin + 1s” | 时间轴,位置参数ex“ + = 1” | GSAP |
翻译自: https://css-tricks.com/smil-is-dead-long-live-smil-a-guide-to-alternatives-to-smil-features/