UNPKG

threepipe

Version:

A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.

190 lines 5.47 kB
import { animate, anticipate, backIn, backInOut, backOut, bounceIn, bounceInOut, bounceOut, circIn, circInOut, circOut, easeIn, easeInOut, easeOut, keyframes, linear, } from '@repalash/popmotion'; import { timeout } from 'ts-browser-helpers'; import { MathUtils } from 'three'; export { animate }; const easeInOutSine = (x) => -(Math.cos(Math.PI * x) - 1) / 2; // eslint-disable-next-line @typescript-eslint/naming-convention export const EasingFunctions = { linear: linear, easeIn: easeIn, easeOut: easeOut, easeInOut: easeInOut, circIn: circIn, circOut: circOut, circInOut: circInOut, backIn: backIn, backOut: backOut, backInOut: backInOut, anticipate: anticipate, bounceOut: bounceOut, bounceIn: bounceIn, bounceInOut: bounceInOut, easeInOutSine: easeInOutSine, }; export function makeSetterFor(target, key, setDirty) { const v = target[key]; const dirty = () => { // if (typeof target?.setDirty === 'function') target.setDirty() setDirty?.(); }; const isBool = typeof v === 'boolean'; if (v && v.isColor) return (a) => { v.set(a); dirty(); }; else if (v && typeof v.copy === 'function') return (a) => { v.copy(a); dirty(); }; else return (a) => { target[key] = !isBool ? a : !!a; dirty(); }; } export async function animateTarget(target, key, options, animations, forceCurrent = false) { if (!(key in target)) { console.error('invalid key', key, target); } const setter = makeSetterFor(target, key); const fromVal = forceCurrent || options.from === undefined ? target[key] : options.from; const onUpdate = (val) => { setter(val); options.onUpdate && options.onUpdate(val); }; if (typeof fromVal === 'boolean') { const { duration } = options; // todo: divide by 2? or support keyframes. return timeout(duration ?? 0).then(() => onUpdate(options.to)); } else { if (typeof options.to === 'function') { options = { ...options, to: options.to(fromVal, target) }; // need to duplicate options } return animateAsync({ ...options, from: fromVal, onUpdate, }, animations); } } export async function animateAsync(options, animations) { const complete = options.onComplete; const stop = options.onStop; const end = options.onEnd; options = { ...options }; return new Promise((resolve, reject) => { const end2 = () => { try { end?.(); } catch (e) { reject(e); return false; } return true; }; options.onComplete = () => { try { complete?.(); } catch (e) { if (!end2()) return; reject(e); return; } if (!end2()) return; resolve(); }; options.onStop = () => { try { stop?.(); } catch (e) { if (!end2()) return; reject(e); return; } if (!end2()) return; resolve(); }; const an = animate(options); if (animations) animations.push(an); }); } export function lerpAngle(a, b, t) { const d = b - a; if (d >= Math.PI) { return a + (d - Math.PI * 2) * t; } else if (d <= -Math.PI) { return a + (d + Math.PI * 2) * t; } else { return a + d * t; } } export const lerp = MathUtils.lerp; /** * Simplified version of popmption animate that supports seeking around the animation. Used in AnimationObject.ts * @param from * @param autoplay * @param driver * @param elapsed * @param onPlay * @param onStop * @param onComplete * @param onUpdate * @param delay * @param canComplete * @param options */ export function animateKeyframes({ from, autoplay = true, driver, elapsed = 0, onPlay, onStop, onComplete, onUpdate, delay = 0, canComplete = true, ...options }) { const { to } = options; let driverControls; const computedDuration = options.duration || 0; let latest; let isComplete = false; const animation = keyframes({ ...options, from, to }); function complete() { driverControls.stop(); onComplete && onComplete(); } function update(delta) { const running = elapsed >= delay; elapsed += delta; if (elapsed < delay) { if (!running) return; } const el = Math.max(0, elapsed - delay); if (!isComplete || el <= (computedDuration || 0)) { const state = animation.next(el); latest = state.value; isComplete = state.done; } onUpdate?.(latest); if (isComplete && canComplete) { complete(); } } function play() { onPlay?.(); driverControls = driver(update); driverControls.start(); } autoplay && play(); return { stop: () => { onStop?.(); driverControls.stop(); }, }; } //# sourceMappingURL=animation.js.map