UNPKG

@visactor/vrender-animate

Version:

This module provides a graph-based animation system for VRender.

213 lines (210 loc) 9.51 kB
import { Step, WaitStep } from "./step"; import { Generator, AnimateStatus, AnimateStepType } from "@visactor/vrender-core"; import { defaultTimeline } from "./timeline"; export class Animate { constructor(id = Generator.GenAutoIncrementId(), timeline = defaultTimeline, slience) { this.id = id, this.status = AnimateStatus.INITIAL, this._timeline = timeline, timeline.addAnimate(this), this.slience = slience, this._startTime = 0, this._duration = 0, this._totalDuration = 0, this._loopCount = 0, this._currentLoop = 0, this._bounce = !1, this._firstStep = null, this._lastStep = null, this._startProps = {}, this._endProps = {}, this._preventAttrs = new Set, this.currentTime = 0, this.interpolateUpdateFunction = null, this.priority = 0; } getStartProps() { return this._startProps; } getEndProps() { return this._endProps; } setTimeline(timeline) { this._timeline = timeline; } getTimeline() { return this._timeline; } get timeline() { return this._timeline; } bind(target) { return this.target = target, this.target.animates || (this.target.animates = new Map), this.target.animates.set(this.id, this), this.onRemove((() => { this.stop(), this.target.animates.delete(this.id); })), this.target.onAnimateBind && !this.slience && this.target.onAnimateBind(this), this.target.animationAttribute || (this.target.animationAttribute = {}), this; } to(props, duration = 300, easing = "linear") { const step = new Step(AnimateStepType.to, props, duration, easing); return step.bind(this.target, this), this.updateStepAfterAppend(step), this; } wait(delay) { const step = new WaitStep(AnimateStepType.wait, {}, delay, "linear"); return step.bind(this.target, this), this.updateStepAfterAppend(step), this; } updateStepAfterAppend(step) { this._firstStep ? (this._lastStep.append(step), this._lastStep = step) : (this._firstStep = step, this._lastStep = step), this.parseStepProps(step), this.updateDuration(); } parseStepProps(step) { this._lastStep && (step.propKeys = step.propKeys || Object.keys(step.props), Object.keys(this._endProps).forEach((key => { var _a; step.props[key] = null !== (_a = step.props[key]) && void 0 !== _a ? _a : this._endProps[key]; })), step.propKeys.forEach((key => { this._endProps[key] = step.props[key]; }))); } reSyncProps() { if (!this._lastStep) return; this._endProps = {}; let currentStep = this._firstStep; for (;currentStep; ) Object.keys(this._endProps).forEach((key => { var _a; currentStep.props[key] = null !== (_a = currentStep.props[key]) && void 0 !== _a ? _a : this._endProps[key]; })), currentStep.propKeys.forEach((key => { this._endProps[key] = currentStep.props[key]; })), currentStep = currentStep.next; } from(props, duration = 300, easing = "linear") { const step = new Step(AnimateStepType.from, props, duration, easing); return this._firstStep ? (this._lastStep.append(step), this._lastStep = step) : (this._firstStep = step, this._lastStep = step), this.updateDuration(), this; } play(customAnimate) { return customAnimate.bind(this.target, this), this.updateStepAfterAppend(customAnimate), this; } pause() { this.status === AnimateStatus.RUNNING && (this.status = AnimateStatus.PAUSED); } resume() { this.status === AnimateStatus.PAUSED && (this.status = AnimateStatus.RUNNING); } onStart(cb) { var _a; cb ? (this._onStart || (this._onStart = []), this._onStart.push(cb)) : (null === (_a = this._onStart) || void 0 === _a || _a.forEach((cb => cb())), Object.keys(this._endProps).forEach((key => { this._startProps[key] = this.target.getComputedAttribute(key); }))); } onEnd(cb) { var _a; cb ? (this._onEnd || (this._onEnd = []), this._onEnd.push(cb)) : null === (_a = this._onEnd) || void 0 === _a || _a.forEach((cb => cb())); } onFrame(cb) { cb && (this._onFrame || (this._onFrame = []), this._onFrame.push(cb)); } onRemove(cb) { var _a; cb ? (this._onRemove || (this._onRemove = []), this._onRemove.push(cb)) : null === (_a = this._onRemove) || void 0 === _a || _a.forEach((cb => cb())); } preventAttr(key) { this._preventAttrs.add(key), delete this._startProps[key], delete this._endProps[key]; let step = this._firstStep; for (;step; ) step.deleteSelfAttr(key), step = step.next; } preventAttrs(keys) { keys.forEach((key => this._preventAttrs.add(key))); } validAttr(key) { return !this._preventAttrs.has(key); } runCb(cb) { var _a; return null === (_a = this._lastStep) || void 0 === _a || _a.onEnd(cb), this; } startAt(t) { return this._startTime = t, this; } customInterpolate(key, ratio, from, to, target, ret) { return !1; } getFromValue() { return this._startProps; } getToValue() { return this._endProps; } stop(type) { let step = this._firstStep; for (;step; ) step.stop(), step = step.next; this.status !== AnimateStatus.END && this.onEnd(), this.status = AnimateStatus.END, this.target && ("start" === type ? this.target.setAttributes(this._startProps) : "end" === type ? this.target.setAttributes(this._endProps) : type && this.target.setAttributes(type)); } release() { this.status = AnimateStatus.END, this._onRemove && this._onRemove.forEach((cb => cb())), this._onStart = [], this._onFrame = [], this._onEnd = [], this._onRemove = []; } getDuration() { return this._duration; } getStartTime() { return this._startTime; } afterAll(list) { if (!list || 0 === list.length) return this; let maxEndTime = 0; return list.forEach((animate => { const endTime = animate.getStartTime() + animate.getTotalDuration(); maxEndTime = Math.max(maxEndTime, endTime); })), this.startAt(maxEndTime); } after(animate) { if (!animate) return this; const endTime = animate.getStartTime() + animate.getTotalDuration(); return this.startAt(endTime); } parallel(animate) { return animate ? (this.startAt(animate.getStartTime()), this) : this; } loop(n) { return !0 === n ? n = 1 / 0 : !1 === n && (n = 0), this._loopCount = n, this.updateDuration(), this; } bounce(b) { return this._bounce = b, this; } advance(delta) { var _a, _b, _c; if (this.status === AnimateStatus.END) return void console.warn("aaa 动画已经结束,不能推进"); const nextTime = this.currentTime + delta; if (nextTime < this._startTime) return void (this.currentTime = nextTime); if (nextTime >= this._startTime + this._totalDuration) return null === (_a = this._lastStep) || void 0 === _a || _a.onUpdate(!0, 1, {}), null === (_b = this._lastStep) || void 0 === _b || _b.onEnd(), this.onEnd(), void (this.status = AnimateStatus.END); this.status = AnimateStatus.RUNNING, this.currentTime <= this._startTime && this.onStart(), this.currentTime = nextTime; let cycleTime = nextTime - this._startTime, newLoop = !1, bounceTime = !1; if (this._loopCount > 0) { cycleTime = (nextTime - this._startTime) % this._duration; const currentLoop = Math.floor((nextTime - this._startTime) / this._duration); newLoop = currentLoop > this._currentLoop, this._currentLoop = currentLoop, bounceTime = this._bounce && currentLoop % 2 == 1, bounceTime && (cycleTime = this._duration - cycleTime); } newLoop && !bounceTime && this.target.setAttributes(this._startProps); let targetStep = null; if (this._lastStep === this._firstStep) targetStep = this._firstStep; else { let currentStep = this._firstStep; for (;currentStep; ) { const stepStartTime = currentStep.getStartTime(), stepDuration = currentStep.getDuration(); if (cycleTime >= stepStartTime && cycleTime <= stepStartTime + stepDuration) { targetStep = currentStep; break; } currentStep = currentStep.next; } } if (!targetStep) return; targetStep !== this.lastRunStep && (null === (_c = this.lastRunStep) || void 0 === _c || _c.onEnd()), this.lastRunStep = targetStep; const ratio = (cycleTime - targetStep.getStartTime()) / targetStep.getDuration(), isEnd = ratio >= 1; targetStep.update(isEnd, ratio, {}), isEnd && (targetStep.onEnd(), this.lastRunStep = null); } updateDuration() { this._lastStep ? (this._duration = this._lastStep.getStartTime() + this._lastStep.getDuration(), this._totalDuration = this._duration * (this._loopCount + 1)) : this._duration = 0; } getTotalDuration() { return this._totalDuration; } getLoop() { return this._loopCount; } } //# sourceMappingURL=animate.js.map