UNPKG

mylingo3d

Version:

Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor

94 lines 3.57 kB
import { Disposable } from "@lincode/promiselikes"; import { AnimationMixer, AnimationClip, NumberKeyframeTrack, LoopRepeat, LoopOnce } from "three"; import { forceGet } from "@lincode/utils"; import { onBeforeRender } from "../../../events/onBeforeRender"; import { dt } from "../../../engine/eventLoop"; const targetMixerMap = new WeakMap(); const mixerActionMap = new WeakMap(); const mixerHandleMap = new WeakMap(); export default class AnimationManager extends Disposable { clip; name; mixer; action; constructor(nameOrClip, target) { super(); this.mixer = forceGet(targetMixerMap, target, () => new AnimationMixer(target)); if (typeof nameOrClip === "string") this.name = nameOrClip; else { this.name = nameOrClip.name; this.loadClip(nameOrClip); } } retarget(target) { const newClip = this.clip.clone(); const targetName = target.name + "."; newClip.tracks = newClip.tracks.filter((track) => track.name.startsWith(targetName)); return new AnimationManager(newClip, target); } dispose() { if (this.done) return this; super.dispose(); this.stop(); return this; } get duration() { return this.clip?.duration ?? 0; } loadClip(clip) { this.clip = clip; this.action = this.mixer.clipAction(clip); } setTracks(data) { const tracks = Object.entries(data).map(([property, frames]) => new NumberKeyframeTrack("." + property, Object.keys(frames).map((t) => Number(t)), Object.values(frames))); this.clip && this.mixer.uncacheClip(this.clip); this.loadClip(new AnimationClip(this.name, -1, tracks)); } play({ crossFade = 0.25, repeat = true, onFinish } = {}) { const [prevAction, prevRepeat] = mixerActionMap.get(this.mixer) ?? []; if (prevAction?.isRunning() && this.action === prevAction) { repeat !== prevRepeat && prevAction.setLoop(repeat ? LoopRepeat : LoopOnce, Infinity); return; } mixerHandleMap.get(this.mixer)?.cancel(); const handle = this.watch(onBeforeRender(() => this.mixer.update(dt[0]))); mixerHandleMap.set(this.mixer, handle); const { action } = this; if (!action) return; if (prevAction && crossFade) { action.time = 0; action.enabled = true; action.crossFadeFrom(prevAction, crossFade, true); } else this.mixer.stopAllAction(); mixerActionMap.set(this.mixer, [action, repeat]); action.setLoop(repeat ? LoopRepeat : LoopOnce, Infinity); action.clampWhenFinished = true; const handleFinish = () => onFinish?.(); this.mixer.addEventListener("finished", handleFinish); handle.then(() => this.mixer.removeEventListener("finished", handleFinish)); action.paused && action.stop(); action.play(); } stop() { this.action && (this.action.paused = true); mixerHandleMap.get(this.mixer)?.cancel(); } getPaused() { return this.action?.paused; } setPaused(val) { this.action && (this.action.paused = val); } update(seconds) { this.mixer.time = 0; this.action && (this.action.time = 0); this.mixer.update(seconds); } } //# sourceMappingURL=AnimationManager.js.map