tween, haha

小说爽文看完了,索然无味。学习一下JS动画吧。

抬头一看,还挺麻烦。(因为怕麻烦,在家好几天没吃顿正经饭,吃的干粮。)

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
/*
* 提取几个函数来看看
* b为初始值,c为变化量。(比如可以变化高度,b初始为100,需要动画到50,c就是-50变化总量。)
* t,d是时间,但按帧数比较容易理解。假设d表示动画总帧数,t就是第t帧。每一帧所用的时间加起来就是动画总时间。哈哈,还是扯蛋到时间上了。
*/

function Linear(t, b, c, d) {
return c*t/d + b;
}

function QuadIn(t, b, c, d) {
return c*(t/=d)*t + b;
}

function QuadOut(t, b, c, d) {
return -c*(t/=d)*(t-2) + b;
}

/*
* 此处省略多行
*/

function BackIn(t, b, c, d, s) {
if(s == undefined) s = 1.70158;
return c*(t/=d)*t( (s+1)*t - s ) + b;
}

/*
* 此处省略几行
*/

废话不多说,直接上图。图呢?还没画好。稍等我去下载一个 gnuplot。啥也不会。

(图片暂时只能凑合。和我想画出来的还有些出入。要想写MathML好像还不行,没用过。麻烦,先不折腾了。)

我们需要构造一个函数 t ↦ st ∈ [0, d],s ∈ R,并且还得满足 0 ↦ bd ↦ b+c。也就是初始(t=0)值为 b,结束(t=d) 值为 b+c。

当然,我们用的时候还需要离散化。t取值 0, 1, 2, …, d。

验证上面 Linear(0, b, c, d) = b, Linear(d, b, c, d) = b+c; BackIn(0, b, c, d, s) = b, BackIn(d, b, c, d, s) = b+c

不过上面的写法看起来有点猥琐。

1
2
3
function QuadIn(t, b, c, d) {
return c*(t/=d)*t + b;
}

其实就是

1
2
3
function QuadIn(t, b, c, d) {
return c*(t/d)*(t/d) + b;
}

动画前,b,c,d都应该确定好。所以上面的函数其实只有一个“真参数” t。(不排除,途中,修改了其他参数,不过这就产生另一个新的函数了。)

设 k = t/d,则 t = kd。这个函数就是 k ↦ tk ∈ [0, 1],t ∈ [0, d],而且 t=kd。 这和上面的函数 t ↦ s 复合一下,就产生新函数 k ↦ sk ∈ [0, 1],s ∈ R,并且还得满足 0 ↦ b1 ↦ b+c。好像也没什么卵用。

我瞅了一眼tween.js中这一部分,它应该是这么弄得。(意思一下,源码不是这样)

1
2
3
4
5
6
7
8
function QuadIn_k(k) {
return k*k;
}

/*大概意思*/
function QuadIn(t,b, c, d) {
return c * QuadIn_k(t/d) + b;
}

修正量 b,c和 k 隔离开了。

说了这么多,我也不知道在讲什么。关键其实是如何构造一个函数?

那我就随便构造一个吧。弄个抛物线吧,取 d/3为对称轴,过 (0, b) (d, b+c)。经过艰苦计算得出(老了,一个抛物线算了一个小时)

t ↦ 3*c/d/d*(t-d/3)^2 + b - c/3

t ↦ -3*c/d/d*(t-d/3)^2 + b + c/3

我们随便娶一个

1
2
3
function QuadFun_1_3(t, b, c, d) {
return 3*c/d/d*(t-d/3)*(t-d/3) + b - c/3;
}

谈不上构造,你只要想办法把弄条曲线,通过 (0, b) (d, b+c) 就行了。

  • 比如 Linear 就是条直线。
  • 比如 QuadIn 就是对称轴为s轴的抛物线。
  • ……
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
<!DOCTYPE html>
<html>
<head>
<title>tween</title>
<style>
#test {
width: 500px;
height: 500px;
background-color: lightgreen;
position: absolute;
left: 500px;
top: 200px;
}

#test > div {
position: absolute;
background-color: red;
width: 3px;
height: 3px;
}
</style>
</head>
<body>
<div id="test"></div>
<script>

function ElasticEaseIn(t,b,c,d,a,p) {
if (t==0) return b;
if ((t/=d)==1) return b+c;
if (!p) p=d*.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;
}

function backIn(t, b, c, d, s) {
if(s===undefined) s=1.70158;
return c*(t/d)*(t/d)*((s+1)*(t/d)-s) + b;
}

// 随便搞一个函数
function QuadFun_1_3(t, b, c, d) {
return 3*c/d/d*(t-d/3)*(t-d/3) + b - c/3;
}

var t = 0;
var b = 0;
var c = 400;
var d = 60;
var result = [];
var raf;
var raf = requestAnimationFrame(function chuizi() {
var div;

if(t <= d) {
p = QuadFun_1_3(t, b, c, d); // 调用不同的动画函数
result.push({t: t, T: Date.now(), p: p});
div = document.createElement('div');
div.style.left = 6*t + "px";
div.style.top = p + "px";
test.appendChild(div);

t++;
requestAnimationFrame(chuizi);
}
});
</script>
</body>
</html>

用js库的时候,就可以自己选个合适的 动画函数 塞进去。