| /** | 
|  * 动画主控制器 | 
|  * @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); | 
|     } | 
| } |