@oiij/use
Version:
Som Composable Functions for Vue 3
69 lines (67 loc) • 1.99 kB
JavaScript
import { createEventHook, useRafFn } from "@vueuse/core";
import { computed, readonly, ref, toValue, watchEffect } from "vue";
//#region src/composables/use-number-animation.ts
const easingFns = {
linear: (t) => t,
easeIn: (t) => t * t,
easeOut: (t) => 1 - (1 - t) ** 2,
easeInOut: (t) => t < .5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2
};
function useNumberAnimation(to, options) {
const { from = 0, manual = false, duration = 1 * 1e3, precision = 2, easing = "linear" } = options ?? {};
const easingFn = typeof easing === "function" ? easing : easingFns[easing] || easingFns.linear;
const currentValue = ref(from);
const targetValue = ref(toValue(to));
watchEffect(() => {
if (targetValue.value !== toValue(to)) {
targetValue.value = toValue(to);
start();
}
});
const startTime = ref(0);
const isFirst = ref(true);
const onStartHook = createEventHook();
const onEndHook = createEventHook();
const onProgressHook = createEventHook();
const { pause, resume, isActive } = useRafFn(() => {
const elapsed = performance.now() - startTime.value;
if (isFirst.value) {
isFirst.value = false;
onStartHook.trigger();
}
const progress = Math.min(elapsed / duration, 1);
const easedProgress = easingFn(progress);
currentValue.value = from + (targetValue.value - from) * easedProgress;
if (progress >= 1) {
currentValue.value = targetValue.value;
pause();
onEndHook.trigger();
return;
}
onProgressHook.trigger(currentValue.value);
}, { immediate: false });
function start() {
startTime.value = performance.now();
isFirst.value = true;
resume();
}
function stop() {
pause();
onEndHook.trigger();
currentValue.value = targetValue.value;
}
if (!manual) start();
return {
value: readonly(computed(() => currentValue.value.toFixed(precision))),
isActive,
start,
stop,
pause,
resume,
onStart: onStartHook.on,
onEnd: onEndHook.on,
onProgress: onProgressHook.on
};
}
//#endregion
export { useNumberAnimation };