UNPKG

tofu.js

Version:

a helper three.js library for building UC-AR

177 lines (157 loc) 3.97 kB
import { EventDispatcher } from 'three'; import Utils from '../utils/Utils'; /** * an animate class, root class * @private */ class Animate extends EventDispatcher { /** * config your animation * @param {Object} options animate config * @private */ constructor(options) { super(); this.object = options.object || {}; this.duration = options.duration || 300; this.living = true; this.resident = options.resident || false; this.infinite = options.infinite || false; this.alternate = options.alternate || false; this.repeats = options.repeats || 0; this.delay = options.delay || 0; this.wait = options.wait || 0; this.timeScale = Utils.isNumber(options.timeScale) ? options.timeScale : 1; if (options.onComplete) { this.on('complete', options.onComplete.bind(this)); } if (options.onUpdate) { this.on('update', options.onUpdate.bind(this)); } this.init(); this.paused = false; } /** * init animate state * @private */ init() { this.direction = 1; this.progress = 0; this.repeatsCut = this.repeats; this.delayCut = this.delay; this.waitCut = this.wait; } /** * update the timeline and get pose * * @private * @param {Number} snippet time snippet * @return {Object} pose state */ update(snippet) { const snippetCache = this.direction * this.timeScale * snippet; if (this.waitCut > 0) { this.waitCut -= Math.abs(snippetCache); return; } if (this.paused || !this.living || this.delayCut > 0) { if (this.delayCut > 0) this.delayCut -= Math.abs(snippetCache); return; } this.progress += snippetCache; let isEnd = false; const progressCache = this.progress; if (this.spill()) { if (this.repeatsCut > 0 || this.infinite) { if (this.repeatsCut > 0) --this.repeatsCut; this.delayCut = this.delay; if (this.alternate) { this.direction *= -1; this.progress = Utils.codomainBounce(this.progress, 0, this.duration); } else { this.direction = 1; this.progress = Utils.euclideanModulo(this.progress, this.duration); } } else { isEnd = true; } } let pose; if (!isEnd) { pose = this.nextPose(); this.emit('update', pose, this.progress / this.duration); } else { if (!this.resident) this.living = false; this.progress = Utils.clamp(progressCache, 0, this.duration); pose = this.nextPose(); this.emit('complete', pose, Math.abs(progressCache - this.progress)); } return pose; } /** * check progress was spill * * @private * @return {Boolean} whether spill or not */ spill() { const bSpill = this.progress <= 0 && this.direction === -1; const tSpill = this.progress >= this.duration && this.direction === 1; return bSpill || tSpill; } /** * get next pose, should be overwrite by sub-class * @private */ nextPose() { console.warn('should be overwrite'); } /** * linear interpolation * @private * @param {number} p0 start value * @param {number} p1 end value * @param {number} t time rate * @return {Number} interpolation value */ linear(p0, p1, t) { return (p1 - p0) * t + p0; } /** * pause this animate */ pause() { this.paused = true; } /** * restore this animate play */ restart() { this.paused = false; } /** * stop this animate at last frame, will trigger `complete` event */ stop() { this.repeats = 0; this.infinite = false; this.progress = this.duration; } /** * set this queue's timeScale * @param {Number} speed set timescale */ setSpeed(speed) { this.timeScale = speed; } /** * cancle this animate, will not trigger `complete` event */ cancle() { this.living = false; } } export default Animate;