Tween.js源码解读

之前想写写关于前端动画的文章,然后重新整理了下动画开源框架,觉得还是有必要出一系列源码解读文章,这篇就来解读下Tween.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
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
// 先是定义一个全局对象,用来统一处理tween对象
var TWEEN = TWEEN || (function(){
var _tweens = [];
return {
getAll: function() {
return _tweens;
},
removeAll: function() {
_tweens = [];
},
add: function(tween) {
_tweens.push(tween);
},
remove: funciton(tween) {
var i = _tweens.indexOf(tween);
_tweens.splice(i, 1);
},
update: function(time, preserve) {
if (_tweens.length === 0) {
return false;
}
var i = 0;
time = time !== undefined ? time : TWEEN.now();
while(i < _tweens.length) {
if (_tweens[i].update(time) || preserve) {
i++;
} else {
_tweens.splice(i, 1);
}
}
return true;
}
};
})();
// 接下来是对TWEEN.now函数的定义和兼容性处理
if () {
} else if () {
}
...省略
//然后在TWEEN对象上定义Tween构造函数,用来创建动画对象
TWEEN.Tween = function() {
// 定义私有变量
...
// 定义私有方法
this.to = function() {
// 处理动画到达点的属性
return this;
};
this.start = function() {
// 开始将tween放入全局对象的_tweens中,并且设置好初始的各种值
return this;
};
//
this.stop = function() {
// 停止此动画,并且删除链在后面的动画对象
return this;
};
this.end = function() {
// 快速执行完动画
this.update(_startTime + _duration);
return this;
};
this.stopChainedTweens = function() {
// 停止链接的动画对象
};
this.delay = function(amount) {
// 延时动画开始执行的时间
_delayTime = amount;
return this;
};
this.repeatDelay = function (amount) {
// 重复延时动画执行时间
_repeatDelayTime = amount;
return this;
};
this.yoyo = function (yoyo) {
// 设置溜溜球效果
_yoyo = yoyo;
return this;
};
this.easing = function(easing) {
// 设置动画效果
_easingFunction = easing;
return this;
};
this.interpolation = function (interpolation) {
// 设置其它绘制函数,如贝塞尔曲线等
_interpolationFunction = interpolation;
return this;
};
this.chain = function () {
// 设置链接动画
_chainedTweens = arguments;
return this;
};
// 接下来是设置各类监听函数
this.onStart = function(callback) {
// 执行开始
_onStartCallback = callback;
return this;
}
this.onUpdate = function (callback) {
// 更新态
_onUpdateCallback = callback;
return this;
};
this.onComplete = function (callback) {
// 完成态
_onCompleteCallback = callback;
return this;
};
this.onStop = function (callback) {
// 停止态
_onStopCallback = callback;
return this;
};
// 最后是动画更新函数,这个函数是精华所在
this.update = function (time) {
var property;
var elapsed;
var value;
if (time < _startTime) {
return true;
}
if (_onStartCallbackFired === false) {
if (_onStartCallback !== null) {
_onStartCallback.call(_object, _object);
}
_onStartCallbackFired = true;
}
elapsed = (time - _startTime) / _duration;
elapsed = elapsed > 1 ? 1 : elapsed;
value = _easingFunction(elapsed);
for (property in _valuesEnd) {
// Don't update properties that do not exist in the source object
if (_valuesStart[property] === undefined) {
continue;
}
var start = _valuesStart[property] || 0;
var end = _valuesEnd[property];
if (end instanceof Array) {
_object[property] = _interpolationFunction(end, value);
} else {
// Parses relative end values with start as base (e.g.: +10, -3)
if (typeof (end) === 'string') {
if (end.charAt(0) === '+' || end.charAt(0) === '-') {
end = start + parseFloat(end);
} else {
end = parseFloat(end);
}
}
// Protect against non numeric properties.
if (typeof (end) === 'number') {
_object[property] = start + (end - start) * value;
}
}
}
if (_onUpdateCallback !== null) {
_onUpdateCallback.call(_object, value);
}
if (elapsed === 1) {
if (_repeat > 0) {
if (isFinite(_repeat)) {
_repeat--;
}
// Reassign starting values, restart by making startTime = now
for (property in _valuesStartRepeat) {
if (typeof (_valuesEnd[property]) === 'string') {
_valuesStartRepeat[property] = _valuesStartRepeat[property] + parseFloat(_valuesEnd[property]);
}
if (_yoyo) {
var tmp = _valuesStartRepeat[property];
_valuesStartRepeat[property] = _valuesEnd[property];
_valuesEnd[property] = tmp;
}
_valuesStart[property] = _valuesStartRepeat[property];
}
if (_yoyo) {
_reversed = !_reversed;
}
if (_repeatDelayTime !== undefined) {
_startTime = time + _repeatDelayTime;
} else {
_startTime = time + _delayTime;
}
return true;
} else {
if (_onCompleteCallback !== null) {
_onCompleteCallback.call(_object, _object);
}
for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
// Make the chained tweens start exactly at the time they should,
// even if the `update()` method was called way past the duration of the tween
_chainedTweens[i].start(_startTime + _duration);
}
return false;
}
}
return true;
};
}
// 接下来定义各类动画函数
TWEEN.Easing = {
// 线性
Linear: {
None: funciton(k) {
return k;
}
},
// 二次方
Quadratic: {
In: function() {
},
Out: function() {
},
InOut: function() {
}
},
// 三次方
Cubic: {
// 都是In,Out,InOut的函数处理
},
// 四次方
Quintic: {},
// 正弦曲线
Sinusoidal: {},
// 指数曲线
Exponential: {},
// 圆弧
Circular: {},
// 伸缩
Elastic: {},
// Back曲线
Back: {},
// 弹跳曲线
Bounce: {}
};
// 接下来是定义一些动画插入函数,用来改变原有动画路径
TWEEN.Interpolation = {
linear: function () {},
Bezier: function() {},
CatmullRom: function() {},
Util: {
Linear: function() {
},
Bernstein: function() {
},
Factorial: function() {
},
CatmullRom: function() {
}
}
}
// 最后是定义模块,使用的是UMD(AMD、CMD的兼容方式)
(function (root) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], function () {
return TWEEN;
});
} else if (typeof module !== 'undefined' && typeof exports === 'object') {
// Node.js
module.exports = TWEEN;
} else if (root !== undefined) {
// Global variable
root.TWEEN = TWEEN;
}
})(this);

大体上就是这些,细节点和动画函数可以再深入研究,另外可以再去扩展其他动画函数。Tween.js的使用方法也很简单,都是链式操作,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var position = {},
endPosition = {},
tween;
init();
animate();
function init() {
tween = new TWEEN.Tween(position).to(endPosition, 2000).delay(1000).easing(Tween.Easing.Elastic.InOut).onUpdate(update).start();
}
function update() {
// 设置目标属性
}
function animate(time) {
requestAnimationFrame(animate);
TWEEN.update(time);
}

分享到