You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
76 lines
1.8 KiB
76 lines
1.8 KiB
import SpringEaser from './spring-easer.js';
|
|
|
|
/** @typedef {import('./animations.js').SharedAnimationProps} SharedAnimationProps */
|
|
|
|
/**
|
|
* @typedef {Object} DefaultSpringAnimationProps
|
|
*
|
|
* @prop {number} start
|
|
* @prop {number} end
|
|
* @prop {number} velocity
|
|
* @prop {number} [dampingRatio]
|
|
* @prop {number} [naturalFrequency]
|
|
* @prop {(end: number) => void} onUpdate
|
|
*/
|
|
|
|
/** @typedef {SharedAnimationProps & DefaultSpringAnimationProps} SpringAnimationProps */
|
|
|
|
class SpringAnimation {
|
|
/**
|
|
* @param {SpringAnimationProps} props
|
|
*/
|
|
constructor(props) {
|
|
this.props = props;
|
|
this._raf = 0;
|
|
|
|
const {
|
|
start,
|
|
end,
|
|
velocity,
|
|
onUpdate,
|
|
onComplete,
|
|
onFinish = () => {},
|
|
dampingRatio,
|
|
naturalFrequency
|
|
} = props;
|
|
|
|
this.onFinish = onFinish;
|
|
|
|
const easer = new SpringEaser(velocity, dampingRatio, naturalFrequency);
|
|
let prevTime = Date.now();
|
|
let deltaPosition = start - end;
|
|
|
|
const animationLoop = () => {
|
|
if (this._raf) {
|
|
deltaPosition = easer.easeFrame(deltaPosition, Date.now() - prevTime);
|
|
|
|
// Stop the animation if velocity is low and position is close to end
|
|
if (Math.abs(deltaPosition) < 1 && Math.abs(easer.velocity) < 50) {
|
|
// Finalize the animation
|
|
onUpdate(end);
|
|
if (onComplete) {
|
|
onComplete();
|
|
}
|
|
this.onFinish();
|
|
} else {
|
|
prevTime = Date.now();
|
|
onUpdate(deltaPosition + end);
|
|
this._raf = requestAnimationFrame(animationLoop);
|
|
}
|
|
}
|
|
};
|
|
|
|
this._raf = requestAnimationFrame(animationLoop);
|
|
}
|
|
|
|
// Destroy is called automatically onFinish
|
|
destroy() {
|
|
if (this._raf >= 0) {
|
|
cancelAnimationFrame(this._raf);
|
|
}
|
|
this._raf = 0;
|
|
}
|
|
}
|
|
|
|
export default SpringAnimation;
|