Javascript的元素运动(一)

开始写之前的话

其实在这篇文章之前写过一次animate,但是再回首时候它几经不堪入目。有些看小时候作文的感觉。那时候把事情想的太复杂。

JS的运动,如果说复杂呢,js运动涉及运动设计到的算法真的是很复杂,然而说简单,其实它在运行机制上,真的很简单很简单。

动画的生成

想了好久,怎样构成一个动画呢?动画有两种,直线运动和曲线,这里仅仅说直线运动的分类

  • 匀速运动
  • 变速运动

匀速的运动很好模拟,使用setInterval来模拟,每隔一段时间对物体的位置累加一个相同的单位即可,那么问题来了,变速匀速怎样处理呢?匀速可以累加,而变速运动类型很多,怎样去处理这个加法的变量?

参考了一下开源方案,最后发现其实处理过程中简单归纳起来其实也不外如是,其实和上面的思路是一样的,它使用函数(内部封装Tween算法)来计算这个变量。具体流程是这样的:

  1. 通过算法计算变量
  2. 通过一个postion封装来处理物体位置
  3. 通过setInterval来每个一段时间调用postion(传入算法执行后的变量)来更新物体位置

在这个过程中,时间是维系和贯穿这个过程的唯一要素,3个过程中都不能缺少它。
为了下次不再为物理动画头疼,这里直接用Tween的算法了,即是总结,也是一个加深和记录。

这里先贴一份Tween算法:

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
var Tween = {
linear: function (t, b, c, d){ //匀速
return c*t/d + b;
},
easeIn: function(t, b, c, d){ //加速曲线
return c*(t/=d)*t + b;
},
easeOut: function(t, b, c, d){ //减速曲线
return -c *(t/=d)*(t-2) + b;
},
easeBoth: function(t, b, c, d){ //加速减速曲线
if ((t/=d/2) < 1) {
return c/2*t*t + b;
}
return -c/2 * ((--t)*(t-2) - 1) + b;
},
easeInStrong: function(t, b, c, d){ //加加速曲线
return c*(t/=d)*t*t*t + b;
},
easeOutStrong: function(t, b, c, d){ //减减速曲线
return -c * ((t=t/d-1)*t*t*t - 1) + b;
},
easeBothStrong: function(t, b, c, d){ //加加速减减速曲线
if ((t/=d/2) < 1) {
return c/2*t*t*t*t + b;
}
return -c/2 * ((t-=2)*t*t*t - 2) + b;
},
elasticIn: function(t, b, c, d, a, p){ //正弦衰减曲线(弹动渐入)
if (t === 0) {
return b;
}
if ( (t /= d) == 1 ) {
return b+c;
}
if (!p) {
p=d*0.3;
}
if (!a || a < Math.abs(c)) {
a = c;
var s = p/4;
} else {
var s = p/(2*Math.PI) * Math.asin (c/a);
}
return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
},
elasticOut: function(t, b, c, d, a, p){ //正弦增强曲线(弹动渐出)
if (t === 0) {
return b;
}
if ( (t /= d) == 1 ) {
return b+c;
}
if (!p) {
p=d*0.3;
}
if (!a || a < Math.abs(c)) {
a = c;
var s = p / 4;
} else {
var s = p/(2*Math.PI) * Math.asin (c/a);
}
return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
},
elasticBoth: function(t, b, c, d, a, p){
if (t === 0) {
return b;
}
if ( (t /= d/2) == 2 ) {
return b+c;
}
if (!p) {
p = d*(0.3*1.5);
}
if ( !a || a < Math.abs(c) ) {
a = c;
var s = p/4;
}
else {
var s = p/(2*Math.PI) * Math.asin (c/a);
}
if (t < 1) {
return - 0.5*(a*Math.pow(2,10*(t-=1)) *
Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
}
return a*Math.pow(2,-10*(t-=1)) *
Math.sin( (t*d-s)*(2*Math.PI)/p )*0.5 + c + b;
},
backIn: function(t, b, c, d, s){ //回退加速(回退渐入)
if (typeof s == 'undefined') {
s = 1.70158;
}
return c*(t/=d)*t*((s+1)*t - s) + b;
},
backOut: function(t, b, c, d, s){
if (typeof s == 'undefined') {
s = 3.70158; //回缩的距离
}
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
},
backBoth: function(t, b, c, d, s){
if (typeof s == 'undefined') {
s = 1.70158;
}
if ((t /= d/2 ) < 1) {
return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
}
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
},
bounceIn: function(t, b, c, d){ //弹球减振(弹球渐出)
return c - Tween['bounceOut'](d-t, 0, c, d) + b;
},
bounceOut: function(t, b, c, d){
if ((t/=d) < (1/2.75)) {
return c*(7.5625*t*t) + b;
} else if (t < (2/2.75)) {
return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
} else if (t < (2.5/2.75)) {
return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
}
return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
},
bounceBoth: function(t, b, c, d){
if (t < d/2) {
return Tween['bounceIn'](t*2, 0, c, d) * 0.5 + b;
}
return Tween['bounceOut'](t*2-d, 0, c, d) * 0.5 + c*0.5 + b;
}
}

这里说下参数:

1
2
3
4
5
t: current time(当前时间)
b: beginning value(初始值)
c: change in value(变化量)
d: duration(持续时间)
个别算法有其他参数,这里仅就共通处做总结

这个不太好理解到底怎么用了,我们来个场景:
现在div#ABC中有个div#move,ABC我相对定位,move为绝对定位且top和left均为0;现在要开始一次运动:
move要在1s内从left:0运行到left:500px的位置。
那么现在t、b、c、d到底是多少呢?t=0,b=0,c=500-0=500,d=1000(ms)

JS Bin on jsbin.com

可以看到这个DEMO中的例子,这里基本仅仅是用的命令式编程没考虑什么复用性,但是整个过程中还是分成了上面说到的三个步骤:算法计算位置,postion函数处理css定位,setIntereval处理按帧刷新。

这作为第一篇就这样了,这一篇仅仅作为变速运动的上手,虽然没法理解Tween算法的意义,但是数学作为科学的基础,交给专业的人处理就好了,我目前仅仅打算做个安静的FrontEnder用全部精力来紧跟前端潮流。

下一篇有时间再总结一下关于运动的其他知识点,这里列一下:

  1. 动画基础理论:刷新率&setTimeout和setInterval精度
  2. 曲线运动和算法整理
  3. 结合闭包做一个可以复用的动画封装