SVG学习(7)

SVG动画

动画原理

SMIL for SVG

定位动画目标
  • Internal Resource Identifier定位
1
<animate xlink:href="url(#rect1)"></animate>
  • 被包含在目标元素里
1
2
3
<rect x="0" ...>
<animate></animate>
</rect>
基本动画
  • 设置要进行动画的属性以及变化范围、时间长度
1
2
3
4
5
6
7
<animate xlink:herf="url(#rect1)"
attributeType="XML"
attributeName="x"
from="10"
to="110"
dur="3s">
</animate>

svg创建默认的大小为300*150

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<style>
html, body, svg{margin: 0;padding: 0;width: 100%;height: 100%}
</style>
<svg>
<rect x="100" y="100" width="100" height="100" fill="red">
<animate id="goright"
attributeType="XML"
attributeName="x"
begin="0; goleft.end + 1s"
from="100"
to="500"
dur="3s"
fill="freeze"
repeatCount="indefinite">
</animate>
<animate id="goleft"
attributeType="XML"
attributeName="x"
begin="goright.end + 1s"
from="500"
to="100"
dur="3s"
fill="freeze"
repeatCount="indefinite">
</animate>
<animate
attributeType="XML"
attributeName="fill"
from="red"
to="yello"
dur="6s"
fill="freeze">
</animate>
</rect>
</svg>

变换动画
  • 设置要进行动画的属性以及变化范围、时间长度
1
2
3
4
5
6
<animateTransform xlink:href="url(#rect1)"
type="translate"
from="0 0"
to="100 100"
dur="3s">
</animateTransform>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<style>
html, body, svg{margin: 0;padding: 0;width: 100%;height: 100%}
</style>
<svg viewBox="-400 -400 800 800">
<rect x="0" y="0" width="100" height="100" fill="red">
<animateTransform id="rotate"
attributeType="XML"
attributeName="transform"
type="rotate"
from="0"
to="360"
dur="3s"
fill="freeze"
repeatCount="indefinite">
</animateTransform>
</rect>
</svg>

示例:点击查看

轨迹移动
  • 设置轨迹路径
1
2
3
4
5
<animateMotion xlink:href="url(#rect1)"
path="M0,0h100v100h-100v-100z"
rotate="auto"
dur="3s">
</animateMotion>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<svg viewBox="-400 -400 800 800">
<rect x="-25" y="-25" width="50" height="50" fill="rgba(0, 255, 255, .6)">
<animateMotion path="M 0 0L 100 100A 200 200 0 1 0 0 -100" dur="3s" rotate="auto">
</animateMotion>
</rect>
<path id="motion-path" d="M 0 0L 100 100A 200 200 0 1 0 0 -100" stroke="gray" fill="none"></path>
</svg>
<svg viewBox="-400 -400 800 800">
<rect x="-25" y="-25" width="50" height="50" fill="rgba(0, 255, 255, .6)">
<animateMotion dur="3s" rotate="auto">
<mpath xlink:href="#motion-path"></mpath>
</animateMotion>
</rect>
<path id="motion-path" d="M 0 0L 100 100A 200 200 0 1 0 0 -100" stroke="gray" fill="none"></path>
</svg>

脚本动画

Scripting Animation

  • 核心思想
    requestAnimationFrame(update)
  • 示例
    力导向图

弹簧模型

  • 两个点之间:
    Fi = k · xi (假设弹性系数是一样的)
  • 可以计算合:
    F = ∑Fi
  • 加速度:
    a = F / m (可以假设每个点质量一样)
  • 速度:
    v = v0 + a · Δt (Δt 为一帧的时间)
  • 位移:
    s = s0 + v · Δt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<style>
html, body, svg {margin: 0;padding: 0;width: 100%;height: 100%;}
</style>
<svg viewBox="-400 -400 800 800">
<path id="links" fill="none" stroke="gray"></path>
</svg>
<script>
// vector.js
(function() {
function Vector(x, y) {
this.x = x || 0;
this.y = y || 0;
}
Vector.prototype = {
constructor: Vector,
square: function() {
return this.x * this.x + this.y * this.y
},
length: function() {
return Math.sqrt(this.square());
},
add: function(q) {
return new Vector(this.x + q.x, this.y + q.y);
},
minus: function(q) {
return new Vector(this.x - q.x, this.y - q.y);
},
multiply: function(scale) {
return new Vector(this.x * scale, this.y * scale);
},
normalize: function(length) {
if(length === undefined) {
length = 1;
}
return this.multiply(length / this.length());
}
};
Vector.fromPoints = function(p1, p2) {
return new Vector(p2.x - p1.x, p2.y - p1.y);
};
window.Vector = Vector;
})();
</script>
<script>
var points = 'a,b,c'.split(',').map(function(name, index, arr) {
return {
name: name,
color: 'hsl(' + (360 * index / arr.length) + ', 100%, 60%)'
};
});
var relation = 300;
var svg = document.querySelector('svg');
var k = 0.05;

var Vector = window.Vector;

function random(min, max) {
return Math.round(min + (max - min) * Math.random());
}

points.forEach(function(point) {
var circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
var x = random(-400, 400);
var y = random(-400, 400);
circle.setAttribute('cx', x);
circle.setAttribute('cy', y);
circle.setAttribute('r', 10);
circle.setAttribute('fill', point.color);

svg.appendChild(circle);

point.circle = circle;
point.s = new Vector(x, y);
point.v = new Vector();
point.a = new Vector();
});

var lastFrameTime = +new Date();

function update() {
var frameTime = +new Date();
var t = frameTime - lastFrameTime;

t /= 100;

// 点位置更新
points.forEach(function(pa) {
var f = new Vector();

// 计算合力
points.forEach(function(pb) {
if (pa == pb) return;

var x = Vector.fromPoints(pa.s, pb.s);
var delta = x.length() - relation;

// f = k * x
f = f.add(x.normalize(delta * k));
});

pa.a = f;
pa.v = pa.v.add(pa.a.multiply(t)).multiply(0.98);
pa.s = pa.s.add(pa.v.multiply(t));

pa.circle.setAttribute('cx', pa.s.x);
pa.circle.setAttribute('cy', pa.s.y);
});

// 连线更新
var linkPath = [];
points.forEach(function(pa) {
var sa = pa.s;
points.forEach(function(pb) {
if (pa == pb) return;
var sb = pb.s;
linkPath = linkPath.concat([
'M', sa.x, sa.y,
'L', sb.x, sb.y
]);
});
});
document.getElementById('links').setAttribute('d', linkPath.join(' '));

lastFrameTime = frameTime;
window.requestAnimationFrame(update);
}

window.requestAnimationFrame(update);
</script>

示例:点击查看

根据慕课网课程整理