UNPKG

zrender

Version:

A lightweight graphic library providing 2d draw for Apache ECharts

267 lines (226 loc) 5.64 kB
/** * Animation main class, dispatch and manage all animation controllers * */ // TODO Additive animation // http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/ // https://developer.apple.com/videos/wwdc2014/#236 import Eventful from '../core/Eventful'; import requestAnimationFrame from './requestAnimationFrame'; import Animator from './Animator'; import Clip from './Clip'; export function getTime() { return new Date().getTime(); } interface Stage { update?: () => void } interface AnimationOption { stage?: Stage } /** * @example * const animation = new Animation(); * const obj = { * x: 100, * y: 100 * }; * animation.animate(node.position) * .when(1000, { * x: 500, * y: 500 * }) * .when(2000, { * x: 100, * y: 100 * }) * .start(); */ export default class Animation extends Eventful { stage: Stage // Use linked list to store clip private _head: Clip private _tail: Clip private _running = false private _time = 0 private _pausedTime = 0 private _pauseStart = 0 private _paused = false; constructor(opts?: AnimationOption) { super(); opts = opts || {}; this.stage = opts.stage || {}; } /** * Add clip */ addClip(clip: Clip) { if (clip.animation) { // Clip has been added this.removeClip(clip); } if (!this._head) { this._head = this._tail = clip; } else { this._tail.next = clip; clip.prev = this._tail; clip.next = null; this._tail = clip; } clip.animation = this; } /** * Add animator */ addAnimator(animator: Animator<any>) { animator.animation = this; const clip = animator.getClip(); if (clip) { this.addClip(clip); } } /** * Delete animation clip */ removeClip(clip: Clip) { if (!clip.animation) { return; } const prev = clip.prev; const next = clip.next; if (prev) { prev.next = next; } else { // Is head this._head = next; } if (next) { next.prev = prev; } else { // Is tail this._tail = prev; } clip.next = clip.prev = clip.animation = null; } /** * Delete animation clip */ removeAnimator(animator: Animator<any>) { const clip = animator.getClip(); if (clip) { this.removeClip(clip); } animator.animation = null; } update(notTriggerFrameAndStageUpdate?: boolean) { const time = getTime() - this._pausedTime; const delta = time - this._time; let clip = this._head; while (clip) { // Save the nextClip before step. // So the loop will not been affected if the clip is removed in the callback const nextClip = clip.next; let finished = clip.step(time, delta); if (finished) { clip.ondestroy(); this.removeClip(clip); clip = nextClip; } else { clip = nextClip; } } this._time = time; if (!notTriggerFrameAndStageUpdate) { // 'frame' should be triggered before stage, because upper application // depends on the sequence (e.g., echarts-stream and finish // event judge) this.trigger('frame', delta); this.stage.update && this.stage.update(); } } _startLoop() { const self = this; this._running = true; function step() { if (self._running) { requestAnimationFrame(step); !self._paused && self.update(); } } requestAnimationFrame(step); } /** * Start animation. */ start() { if (this._running) { return; } this._time = getTime(); this._pausedTime = 0; this._startLoop(); } /** * Stop animation. */ stop() { this._running = false; } /** * Pause animation. */ pause() { if (!this._paused) { this._pauseStart = getTime(); this._paused = true; } } /** * Resume animation. */ resume() { if (this._paused) { this._pausedTime += getTime() - this._pauseStart; this._paused = false; } } /** * Clear animation. */ clear() { let clip = this._head; while (clip) { let nextClip = clip.next; clip.prev = clip.next = clip.animation = null; clip = nextClip; } this._head = this._tail = null; } /** * Whether animation finished. */ isFinished() { return this._head == null; } /** * Creat animator for a target, whose props can be animated. */ // TODO Gap animate<T>(target: T, options: { loop?: boolean // Whether loop animation }) { options = options || {}; // Start animation loop this.start(); const animator = new Animator( target, options.loop ); this.addAnimator(animator); return animator; } }