svelte-image-viewer
Version:
A couple of simple components for displaying content with pan and zoom capabilities.
74 lines (73 loc) • 2.57 kB
JavaScript
/**
* Smoothly animates a value towards a target using requestAnimationFrame.
*
* @param initialValue - The initial value to start from
* @param smoothing - How quickly to approach the target (0-1, lower = smoother/slower)
* @param threshold - Stop animating when within this distance of target
* @returns Object with current value state and update function
*/
export function moveTowards(initialValue = 0, smoothing = 0.125, threshold = 0.00125) {
const getInitialValue = typeof initialValue === "function" ? initialValue : () => initialValue;
if (typeof window === "undefined") {
return {
get current() {
return getInitialValue();
},
get target() {
return getInitialValue();
},
set target(value) {
void value;
},
destroy() {
void 0;
},
};
}
let currentValue = $state(getInitialValue());
let targetValue = $derived(getInitialValue());
$effect(() => (void targetValue, startAnimation()));
let animationId = null;
let lastTime = window.performance.now();
const animate = (currentTime) => {
const deltaTime = currentTime - lastTime;
lastTime = currentTime;
// Normalise delta time to 60 fps. This ensures consistent animation speed regardless of frame rate
const normalisedDelta = deltaTime / (50 / 3);
const difference = targetValue - currentValue;
// Check if we're close enough to the target
if (Math.abs(difference) < threshold) {
currentValue = targetValue;
animationId = null;
return;
}
// Smooth interpolation with frame-rate independence
const step = difference * smoothing * normalisedDelta;
currentValue += step;
animationId = window.requestAnimationFrame(animate);
};
const startAnimation = () => {
if (animationId === null) {
lastTime = window.performance.now();
animationId = window.requestAnimationFrame(animate);
}
};
const stopAnimation = () => {
if (animationId !== null) {
window.cancelAnimationFrame(animationId);
animationId = null;
}
};
return {
get current() {
return currentValue;
},
get target() {
return targetValue;
},
set target(value) {
targetValue = value;
},
destroy: stopAnimation,
};
}