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
JavaScript
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