-
Notifications
You must be signed in to change notification settings - Fork 0
Svg之animate
[TOC]
http://oreillymedia.github.io/svg-essentials-examples/ch12/simple_animation.html
<rect x="10" y="10" width="200" height="20" stroke="black" fill="none">
<animate id="animation"
attributeName="width"
attributeType="XML"
from="200" to="20"
begin="0s" dur="5s"
fill="freeze" />
</rect><animate> 元素指定了下列信息。
-
attributeName,动画中应该持续改变的值;在这里就是 width。 -
attributeType。width属 性 是 一 个XML属 性。 另 一 个 常 用 的attributeType值 是CSS,表示我们想要改变的属性是一个CSS属性。如果忽略这一属性,它的默认值是auto;它首先会搜索CSS属性,然后才是XML属性。 - 属性的起始(
from)和结束(to)值。在这个例子中,起始值是 200,结束值是 20。from值是可选的;如果不指定,则会使用父元素的值。此外,还有一个by属性,可以代替to,它是一个从from值开始的偏移量;动画结束时属性的值为结束值。 - 动画的开始时间和持续时间。在这个例子中,时间以秒为单位,通过在数字后面使用
s指定。 - 动画结束时做什么。在这个例子中,持续 5 秒之后,属性会“冻结”(
freeze)为to值。也就是 SMIL fill 属性,它会告诉动画引擎如何填补剩下的时间。不要把它跟SVG的fill属性混淆了,该属性用于告诉 SVG 如何描绘对象。如果我们移除这一行,会使用 默认值(remove),5 秒的动画完成之后 width 属性会返回它的原始值 200。
http://oreillymedia.github.io/svg-essentials-examples/ch12/multiple_animation.html
<svg width="275" height="275">
<rect x="10" y="10" width="20" height="20"
style="stroke: black; fill: green; style: fill-opacity: 0.25;">
<animate attributeName="width" attributeType="XML"
from="20" to="200" begin="0s" dur="8s" fill="freeze"/>
<animate attributeName="height" attributeType="XML"
from="20" to="150" begin="0s" dur="8s" fill="freeze"/>
<animate attributeName="fill-opacity" attributeType="CSS"
from="0.25" to="1" begin="0s" dur="3s" fill="freeze"/>
<animate attributeName="fill-opacity" attributeType="CSS"
from="1" to="0.25" begin="3s" dur="3s" fill="freeze"/>
</rect>
</svg上面示例更复杂一些。它从一个 20 乘 20 的绿色方块开始,将在 8 秒的时间里增长为 250 乘 200。前 3 秒,绿色的透明度会增加,接下来 3 秒会减小。注意 fill-opacity 是使用 attributeType="CSS" 的,因为它设置在 style 属性中。
http://oreillymedia.github.io/svg-essentials-examples/ch12/multiple_animation2.html
<svg width="200" height="200">
<rect x="10" y="10" width="20" height="20"
style="stroke: black; fill: #cfc;">
<animate attributeName="width" attributeType="XML"
begin="0s" dur="8s" from="20" to="120" fill="freeze"/>
<animate attributeName="height" attributeType="XML"
begin="0s" dur="8s" from="20" to="120" fill="freeze"/>
</rect>
<circle cx="70" cy="70" r="20"
style="fill: #ccf; stroke: black;">
<animate attributeName="r" attributeType="XML"
begin="2s" dur="4s" from="20" to="50" fill="freeze"/>
</circle>
</svg>上面示例是:一个正方形和圆形动画。正方形尺寸会在 8 秒的时间里 从 20 乘 20 扩大到 120 乘 120。动画开始 2 秒之后,圆形的半径在 4 秒的时间里从 20 扩大 到 50
SVG 动画用到的动画时钟在 SVG 加载完成时开始启动计时,当用户离开页面时停止计时。 我们可以以下列任意一种方式指定特定动画的开始和持续时间为一个数值。
- 时、分、秒形式(1:20:23)的完整时间值。
- 分、秒形式(02:15)的局部时间值。
- 以 h(时)、min(分)、s(秒)或者 ms(毫秒)缩写结尾的时间值,比如 dur="3.5s" begin="1min"。不可以在值和单位之间插入任何空白。
如果不指定单位,默认为秒。
我们可以绑定动画的开始时间为另一个动画的开始或者结束,而不是在文档加载时定义每个动画的开始时间。
<svg width="200" height="200">
<circle cx="60" cy="60" r="30" style="fill: #f9f; stroke: gray;">
<animate id="c1" attributeName="r" attributeType="XML"
begin="0s" dur="4s" from="30" to="10" fill="freeze"/>
</circle>
<circle cx="120" cy="60" r="10" style="fill: #9f9; stroke: gray;">
<animate attributeName="r" attributeType="XML"
begin="c1.end" dur="4s" from="10" to="30" fill="freeze"/>
</circle>
</svg>http://oreillymedia.github.io/svg-essentials-examples/ch12/sync_with_offset.html
<svg width="200" height="200">
<circle cx="60" cy="60" r="30" style="fill: #f9f; stroke: gray;">
<animate id="c1" attributeName="r" attributeType="XML"
begin="0s" dur="4s" from="30" to="10" fill="freeze"/>
</circle>
<circle cx="120" cy="60" r="10" style="fill: #9f9; stroke: gray;">
<animate attributeName="r" attributeType="XML"
begin="c1.begin+1.25s" dur="4s" from="10" to="30" fill="freeze"/>
</circle>
</svg>下面这个动画会在页面载入 6 秒之后开始。它会持续 12 秒或者直到名为 otherAnim 的动画结束,这取决于是动画先结束还是 12 秒先到:
<animate attributeName="width" attributeType="XML" begin="6s" dur="12s" end="otherAnim.end" from="10" to="100" fill="freeze"/>当然,我们也可以设置 end 的值为一个指定的时间;这可以用于在中途拦截动画,这样我 们就可以看看是否一切都在正确的位置。下面这个动画在 5 秒时开始,并且应该持续 10 秒,但是会在文档载入后 9 秒(动画开始 4 秒后)暂停。这 个动画会停止在 40% 处,因此宽度会冻结在 140(从 100 到 200 的 40% 的距离)。
<animate attributeName="width" attributeType="XML" begin="5s" dur="10s" end="9s" from="100" to="200" fill="freeze"/>到目前为止,动画都只发生一次;fill 被设置为 freeze 使动画保持在最后阶段。如果想
要对象返回到动画起始状态,忽略这个属性即可(等价于设置 fill 为默认值 remove)。
-
repeatCount设置一个整型值,告诉引擎我们想要将指定的动画重复多少次。 -
repeatDur,设置一个值,告诉引擎重复应该 持续多长时间。
http://oreillymedia.github.io/svg-essentials-examples/ch12/repeated_action.html
<svg width="350" height="120">
<circle cx="60" cy="60" r="30" style="fill: none; stroke: red;">
<animate attributeName="cx" attributeType="XML"
begin="0s" dur="5s" repeatCount="2"
from="60" to="260" fill="freeze"/>
</circle>
<circle cx="260" cy="90" r="30" style="fill: #ccf; stroke: black;">
<animate attributeName="cx" attributeType="XML"
begin="0s" dur="5s" repeatDur="8s"
from="260" to="60" fill="freeze"/>
</circle>
</svg>http://oreillymedia.github.io/svg-essentials-examples/ch12/animate_color.html
<svg width="150" height="100">
<circle cx="60" cy="60" r="30"
style="fill: #ff9; stroke: gray; stroke-width: 10;">
<animate attributeName="fill"
begin="2s" dur="4s" from="#ff9" to="red" fill="freeze"/>
<animate attributeName="stroke"
begin="2s" dur="4s" from="gray" to="blue" fill="freeze"/>
</circle>
</svg>http://oreillymedia.github.io/svg-essentials-examples/ch12/animate_path_poly.html
<svg width="250" height="250"
xmlns="http://www.w3.org/2000/svg">
<polygon points="30 30 70 30 90 70 10 70"
style="fill:#fcc; stroke:black">
<animate id="animation"
attributeName="points"
attributeType="XML"
to="50 30 70 50 50 90 30 50"
begin="0s" dur="5s" fill="freeze" />
</polygon>
<path d="M15 50 Q 40 15, 50 50, 65 32, 100 40"
style="fill:none; stroke: black" transform="translate(0,50)">
<animate attributeName="d"
attributeType="XML"
to="M50 15 Q 15 40, 50 50, 32 65, 40 100"
begin="0s" dur="5s" fill="freeze"/>
</path>
</svg>我们可以为路径数据或者多边形的点应用动画,只要我们维持点的数量和路径片段的类型即可
目前为止呈现的所有动画元素都提供了一个起始(from 或默认)值和结束(to)值,然后让计算机计算如何从起始值到结束值。我们还可以给动画提供一个特定的中间值,从而允 许用一个独立的 <animate> 元素定义复杂的变化序列。我们可以提供一个用分号分隔的、动画在持续时间内使用的值列表
http://oreillymedia.github.io/svg-essentials-examples/ch12/animating_values.html
<svg width="100" height="100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="30"
style="fill: #ff9; stroke:black;">
<animate attributeName="fill"
begin="2s" dur="4s" values="#ff9;#99f;#f99;#9f9"
fill="freeze"/>
</circle>
</svg>所有这些动画归根结底都是修改值。有时候,特别是对于非数字属性或者不能过渡的属性,我们可能想要在动画序列的某个点上改变某个值。
比如,我们可能想要一个初始不可见的文本项,使它在某个时间变得可见;这里并不需要 from 和 to。因此,SVG 引入了方便速记的 <set> 元素,它只需要一个 to 属性以及适当的时间信息。
http://oreillymedia.github.io/svg-essentials-examples/ch12/animation_set.html
<svg width="150" height="100">
<circle cx="60" cy="60" r="30" style="fill: #ff9; stroke: gray;">
<animate id="c1" attributeName="r" attributeType="XML"
begin="0s" dur="4s" from="30" to="0" fill="freeze"/>
</circle>
<text text-anchor="middle" x="60" y="60" style="visibility: hidden;">
<set attributeName="visibility" attributeType="CSS"
to="visible" begin="4.5s" dur="1s" fill="freeze"/>
All gone!
</text>
</svg><animate> 元素并不适用于旋转、平移、缩放或倾斜变换,因为它们都“被包裹”在 transform 属性内。<animateTransform> 元素就是用来解决这个问题的。我们可以设置 它的 attributeName 为 transform。然后用 type 属性的值指定变换的哪个值应该变化
(translate、scale、rotate、skewX 或者 skewY 之一)。from 和 to 的值指定为适当的要动画绘制的变换。撰写本文时,大多数实现当前都只支持在 XML transform 属性上使用 <animateTransform> 而不支持 CSS3 变换。
http://oreillymedia.github.io/svg-essentials-examples/ch12/animate_transform.html
<svg width="200" height="100">
<g transform="translate(100,60)">
<rect x="-10" y="-10" width="20" height="20"
style="fill: #ff9; stroke: black;">
<animateTransform attributeType="XML"
attributeName="transform" type="scale"
from="1" to="4 2"
begin="0s" dur="4s" fill="freeze"/>
</rect>
</g>
</svg>如果想要对直线运动使用 <animateMotion>,简单地设置 from 和 to 属性,然后给每个属 性分配一对 (x, y) 坐标即可。这个坐标用于指定要移动形状的坐标系统中 (0,0) 点的位置, 类似于translate(x, y)的工作原理。
http://oreillymedia.github.io/svg-essentials-examples/ch12/linear_animateMotion.html
<svg width="120" height="100" viewBox="0 0 120 100">
<g>
<rect x="0" y="0" width="30" height="30" style="fill: #ccc;"/>
<circle cx="30" cy="30" r="15" style="fill: #cfc; stroke: green;"/>
<animateMotion from="0,0" to="60,30" dur="4s" fill="freeze"/>
</g>http://oreillymedia.github.io/svg-essentials-examples/ch12/complex_animate_motion.html
<svg width="250" height="250">
<!-- show the path along which the triangle will move -->
<path d="M50,125 C 100,25 150,225, 200, 125"
style="fill: none; stroke: blue;"/>
<!-- Triangle to be moved along the motion path.
It is defined with an upright orientation with the base of
the triangle centered horizontally just above the origin. -->
<path d="M-10,-3 L10,-3 L0,-25z" style="fill: yellow; stroke: red;">
<animateMotion
path="M50,125 C 100,25 150,225, 200, 125"
dur="6s" fill="freeze"/>
</path>
</svg>http://oreillymedia.github.io/svg-essentials-examples/ch12/animate_motion_rotate.html
<svg width="250" height="250">
<!-- show the path along which the triangle will move -->
<path d="M50,125 C 100,25 150,225, 200, 125"
style="fill: none; stroke: blue;"/>
<!-- Triangle to be moved along the motion path.
It is defined with an upright orientation with the base of
the triangle centered horizontally just above the origin. -->
<path d="M-10,-3 L10,-3 L0,-25z" style="fill: yellow; stroke: red;" >
<animateMotion
path="M50,125 C 100,25 150,225, 200, 125"
rotate="auto"
dur="6s" fill="freeze"/>
</path>
</svg>如果希望对象倾斜以使其 x 轴始终平行于路径的方向,只要给 <animateMotion> 元素添加 一个值为 auto 的 rotate 属性就行了
当我们不使用 rotate 属性时,其默认值为 0,此时对象就像一个沿着路径浮动
的热气球。如果设置 rotate 为 auto,对象就像坐过山车一样,沿着路行向上或向下倾斜。 我们也可以设置 roate 为一个数值,这会使对象在动画中旋转。因此,如果希望无论路径是什么方向,对象都旋转 45 度,就使用 rotate="45"
<svg width="250" height="250">
<path id="cubicCurve" d="M50,125 C 100,25 150,225, 200, 125" style="fill: none; stroke: blue;"/>
<path d="M-10,-3 L10,-3 L0,-25z" style="fill: yellow; stroke: red;" >
<animateMotion rotate="auto" dur="6s" fill="freeze">
<mpath xlink:href="#cubicCurve"/>
</animateMotion>
</path>
</svg>http://oreillymedia.github.io/svg-essentials-examples/ch12/svg_css_anim2.html
<svg width="200" height="200" viewBox="0 0 200 200">
<defs>
<g id="starDef">
<path d="M 38.042 -12.361 9.405 -12.944 -0.000 -40.000
-9.405 -12.944 -38.042 -12.361 -15.217 4.944
-23.511 32.361 0.000 16.000 23.511 32.361 15.217 4.944 Z"/>
</g>
</defs>
<use id="star" class="starStyle" xlink:href="#starDef"
transform="translate(100, 100)"
style="fill: #008000; stroke: #008000"/>
</svg>.starStyle {
animation-name: starAnim;
animation-duration: 2s;
animation-iteration-count: 4;
animation-direction: alternate;
animation-timing-function: ease;
animation-play-state: running;
}
@keyframes starAnim {
0% {
fill-opacity: 1.0;
stroke-width: 0;
}
20% {
transform: translate(100px, 50px)
}
50% {
transform: translate(100px, 50px) rotate(180deg)
}
80% {
transform: translate(100px, 50px) rotate(360deg)
}
100% {
fill-opacity: 0.0;
stroke-width: 6;
}
}<style>
.fill{
fill: blue;
fill-opacity: 0.5;
}
.an {
stroke-width:3;
stroke:red;
stroke-linecap: round;
animation: lineMove 3s ease-out infinite;
}
@keyframes lineMove {
0% {
stroke-dasharray: 0, 740;
}
100% {
stroke-dasharray: 740, 740;
opacity: 1;
}
}
</style>
<svg width="580" height="400" xmlns="http://www.w3.org/2000/svg">
<path class="an fill" id="svg_1" d="m63.5,128.4375c56,-58 98,18.125 115.5,17.5625c17.5,-0.5625 41.5,-74.5625 103.5,-27.5625c62,47 -86,194 -104,194c-18,0 -171,-126 -115,-184z" stroke-width="1.5" stroke="#000" fill="#fff"/>
</svg>
<script>
let obj = document.querySelector("#svg_1");
let length = obj.getTotalLength();
console.log(length); // 740
</script><svg width="100" height="100" viewBox="0 0 100 100">
<path d="
M 50 50
m 0 47
a 47 47 0 1 1 0 -94
a 47 47 0 1 1 0 94
" stroke="#e5e9f2" stroke-width="4.8" fill="none" class="el-progress-circle__track" style="stroke-dasharray: 221.482px, 295.31px; stroke-dashoffset: -36.9137px;"></path>
<path d="
M 50 50
m 0 47
a 47 47 0 1 1 0 -94
a 47 47 0 1 1 0 94
" stroke="#e6a23c" fill="none" stroke-linecap="round" stroke-width="4.8" id="svg_1"
class="el-progress-circle__path"
style="stroke-dasharray: 0px, 295.31px; stroke-dashoffset: -36.9137px; transition: stroke-dasharray 0.6s ease 0s, stroke 0.6s ease 0s;"></path>
</svg>
<button id="add">增加</button>
<script>
let obj = document.querySelector("#svg_1")
let length = obj.getTotalLength()
length = 221.482
let current = 0
let add = document.getElementById('add')
add.addEventListener('click', function () {
current += 10
let value = current * length * 0.01
if(value > length) value = length
obj.style.strokeDasharray = `${value}px ${length}px`
})
</script>Snap 的做动画主要有两种方式:
- 使用
Element里的animate方法,Element.animate(attrs, duration, [easing], [callback]) - 使用 Snap 的静态方法,
Snap.animate(from, to, setter, duration, [easing], [callback])
// 动画样例1
var svg = Snap('#svg');
svg.select('circle').animate({r: 100}, 1000, mina.easeout(), function() {
console.log('animation end');
});
// 动画样例2
var svg = Snap('#svg');
var circle = svg.select('circle');
var rect = svg.select('rect');
Snap.animate(0, 100, function(val) {
circle.attr({r: val});
rect.attr({x: val});
}, 1000, mina.easeout(), function() {
console.log('animation end');
});- 简单数值类,如坐标、宽高、opacity、大部分 Paper API 可配置的属性值,甚至滤镜相关的属性。如{x:100} -> {x:200}, {width:0} -> {width:100}
- path 相关动画,如d属性(变形动画)、描边动画、路径跟随动画
- matrix 类,放大缩小、位移、旋转等,和 CSS 的 transform 类似
- 颜色类,颜色变换动画,如 fill、stroke 属性,如{fill:’#f00’} -> {fill:’#f0f’}
// 动画样例,颜色变化动画
var svg = Snap('#svg');
var circle = svg.paper.circle({cx: 100, cy: 100, r: 50, fill: '#f00'});
circle.animate({fill: '#00f'}, 1000, mina.easeout(), function() {
console.log('animation end');
});path 的d属性在 Snap 的解析规则里可以通过一系列的数学运算,动画中通过插值,达到最终态的d值,不过中间的插值计算我们无法干预。
http://jdc.jd.com/demo/snapsvg/index.html#demo3
http://jdc.jd.com/demo/snapsvg/index.html#demo3-1
// 开始态
var path = svg.paper.path({d: 'M0.500,65.500 C18.680,33.758 45.141,-6.797 72.500,2.500 C99.859,11.797 72.148,59.027 79.500,98.500 C86.852,137.973 117.668,128.914 138.500,59.500 C159.332,-9.914 246.500,59.500 246.500,59.500 C273.181,117.750 137.350,184.417 225.500,173.500 C351.137,157.940 155.369,160.617 162.500,86.500 C165.180,58.645 237.169,-2.418 283.500,2.500 C357.654,10.371 363.758,80.355 364.500,109.500',stroke:'#f00', fill: 'rgba(0,0,0,0)'});
setTimeout(function() {
// 终止态:曲线变直
// path.animate({d: 'M1,100 L350,100'}, 1000, mina.easeout(), function() {
// console.log('animation end');
// });
// 终止态:心形
path.animate({d: 'M114.500,58.500 C106.230,58.751 23.907,-37.262 5.500,21.500 C-26.759,124.483 111.761,221.360 119.500,219.500 C154.464,211.096 201.234,149.580 220.500,104.500 C250.260,34.864 220.892,7.159 194.500,1.500 C160.455,-5.800 122.344,58.262 114.500,58.500 Z'}, 1000, mina.easeout(), function() {
console.log('animation end');
});
}, 1000);http://jdc.jd.com/demo/snapsvg/index.html#demo5
var length = Snap.path.getTotalLength(path); // 获取path的长度
Snap.animate(0, length, function(val) {
var point = Snap.path.getPointAtLength(path, val); // 根据path长度变化获取坐标
var m = new Snap.Matrix();
m.translate(point.x, point.y);
m.rotate(point.alpha - 90); // 使飞机总是朝着曲线方向。point.alpha:点的切线和水平线形成的夹角
plane.transform(m);
}, 30000, mina.easeout(), function() {
console.log('animation end');
});Snap 的 matrix 动画包含各位熟悉的 translate/scale/rotate/skew 动画,原理和 CSS 的 transform 也几乎一致。