@studiometa/js-toolkit
Version:
A set of useful little bits of JavaScript to boost your project! 🚀
115 lines (114 loc) • 3.2 kB
JavaScript
import { cubicBezier } from "@motionone/easing";
import { lerp, map, clamp01, damp, inertiaFinalValue, spring } from "./math/index.js";
import { isDefined, isArray, isNumber } from "./is.js";
import { noop, noopValue as linear } from "./noop.js";
import { useRaf } from "../services/RafService.js";
let id = 0;
const DEFAULT_PROGRESS_PRECISION = 1 / 1e4;
function normalizeEase(ease) {
if (!isDefined(ease)) {
return linear;
}
if (isArray(ease)) {
return cubicBezier(...ease);
}
return ease;
}
function tween(callback, options = {}) {
const raf = useRaf();
let progressValue = 0;
let easedProgress = 0;
let velocity = 0;
const isSmooth = isDefined(options.smooth) && (isNumber(options.smooth) || options.smooth);
const isSpring = isDefined(options.spring) && (isNumber(options.spring) || options.spring);
const stiffness = isNumber(options.spring) ? options.spring : 0.1;
const mass = isNumber(options.mass) ? options.mass : 1;
const damping = isNumber(options.smooth) ? options.smooth : 0.1;
const precision = options.precision ?? DEFAULT_PROGRESS_PRECISION;
const ease = normalizeEase(options.easing);
let delay = options.delay ?? 0;
delay *= 1e3;
let duration = options.duration ?? 1;
duration *= 1e3;
let startTime = getStartTime();
let endTime = getEndTime(startTime);
const key = `tw-${id}`;
id += 1;
const { onStart = noop, onProgress = noop, onFinish = noop } = options;
let isRunning = false;
function pause() {
isRunning = false;
raf.remove(key);
}
function progress(newProgress) {
if (!isDefined(newProgress)) {
return easedProgress;
}
let shouldStop = false;
progressValue = newProgress;
if (isSpring) {
[easedProgress, velocity] = spring(
progressValue,
easedProgress,
velocity,
stiffness,
damping,
mass,
precision
);
} else {
easedProgress = isSmooth ? damp(progressValue, easedProgress, damping, precision) : ease(progressValue);
}
if (Math.abs(1 - easedProgress) < precision) {
progressValue = 1;
easedProgress = 1;
shouldStop = true;
}
callback(easedProgress);
onProgress(easedProgress);
if (shouldStop) {
pause();
requestAnimationFrame(() => onFinish(easedProgress));
}
return easedProgress;
}
function tick(props) {
progress(clamp01(map(props.time, startTime, endTime, 0, 1)));
}
function getStartTime() {
return performance.now() + delay;
}
function getEndTime(time) {
return isSmooth ? inertiaFinalValue(time, 1) : time + duration;
}
function start() {
onStart(0);
startTime = getStartTime();
endTime = getEndTime(startTime);
progressValue = 0;
easedProgress = 0;
isRunning = true;
raf.add(key, tick);
}
function play() {
if (isRunning) {
return;
}
startTime = getStartTime() - lerp(0, duration, progressValue);
endTime = getEndTime(startTime);
isRunning = true;
raf.add(key, tick);
}
return {
start,
finish: () => progress(1),
pause,
play,
progress
};
}
export {
normalizeEase,
tween
};
//# sourceMappingURL=tween.js.map