Clip.ts
3.57 KB
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
/**
* 动画主控制器
* @config target 动画对象,可以是数组,如果是数组的话会批量分发onframe等事件
* @config life(1000) 动画时长
* @config delay(0) 动画延迟时间
* @config loop(true)
* @config onframe
* @config easing(optional)
* @config ondestroy(optional)
* @config onrestart(optional)
*
* TODO pause
*/
import easingFuncs, {AnimationEasing} from './easing';
import type Animation from './Animation';
import { isFunction, noop } from '../core/util';
import { createCubicEasingFunc } from './cubicEasing';
type OnframeCallback = (percent: number) => void;
type ondestroyCallback = () => void
type onrestartCallback = () => void
export type DeferredEventTypes = 'destroy' | 'restart'
// type DeferredEventKeys = 'ondestroy' | 'onrestart'
export interface ClipProps {
life?: number
delay?: number
loop?: boolean
easing?: AnimationEasing
onframe?: OnframeCallback
ondestroy?: ondestroyCallback
onrestart?: onrestartCallback
}
export default class Clip {
private _life: number
private _delay: number
private _inited: boolean = false
private _startTime = 0 // 开始时间单位毫秒
private _pausedTime = 0
private _paused = false
animation: Animation
loop: boolean
easing: AnimationEasing
easingFunc: (p: number) => number
// For linked list. Readonly
next: Clip
prev: Clip
onframe: OnframeCallback
ondestroy: ondestroyCallback
onrestart: onrestartCallback
constructor(opts: ClipProps) {
this._life = opts.life || 1000;
this._delay = opts.delay || 0;
this.loop = opts.loop || false;
this.onframe = opts.onframe || noop;
this.ondestroy = opts.ondestroy || noop;
this.onrestart = opts.onrestart || noop;
opts.easing && this.setEasing(opts.easing);
}
step(globalTime: number, deltaTime: number): boolean {
// Set startTime on first step, or _startTime may has milleseconds different between clips
// PENDING
if (!this._inited) {
this._startTime = globalTime + this._delay;
this._inited = true;
}
if (this._paused) {
this._pausedTime += deltaTime;
return;
}
const life = this._life;
let elapsedTime = globalTime - this._startTime - this._pausedTime;
let percent = elapsedTime / life;
// PENDING: Not begin yet. Still run the loop.
// In the case callback needs to be invoked.
// Or want to update to the begin state at next frame when `setToFinal` and `delay` are both used.
// To avoid the unexpected blink.
if (percent < 0) {
percent = 0;
}
percent = Math.min(percent, 1);
const easingFunc = this.easingFunc;
const schedule = easingFunc ? easingFunc(percent) : percent;
this.onframe(schedule);
// 结束
if (percent === 1) {
if (this.loop) {
// Restart
const remainder = elapsedTime % life;
this._startTime = globalTime - remainder;
this._pausedTime = 0;
this.onrestart();
}
else {
return true;
}
}
return false;
}
pause() {
this._paused = true;
}
resume() {
this._paused = false;
}
setEasing(easing: AnimationEasing) {
this.easing = easing;
this.easingFunc = isFunction(easing)
? easing
: easingFuncs[easing] || createCubicEasingFunc(easing);
}
}