TelegramPWA/src/util/animation.ts
2022-01-24 04:55:59 +01:00

96 lines
2.6 KiB
TypeScript

import { fastRaf } from './schedulers';
interface AnimationInstance {
isCancelled: boolean;
}
let currentInstance: AnimationInstance | undefined;
export function animateSingle(tick: Function, instance?: AnimationInstance) {
if (!instance) {
if (currentInstance && !currentInstance.isCancelled) {
currentInstance.isCancelled = true;
}
instance = { isCancelled: false };
currentInstance = instance;
}
if (!instance!.isCancelled && tick()) {
fastRaf(() => {
animateSingle(tick, instance);
});
}
}
export function animate(tick: Function) {
if (tick()) {
fastRaf(() => {
animate(tick);
});
}
}
export type TimingFn = (t: number) => number;
export type AnimateNumberProps = {
to: number | number[];
from: number | number[];
duration: number;
onUpdate: (value: any) => void;
timing?: TimingFn;
onEnd?: () => void;
};
export const timingFunctions = {
linear: (t: number) => t,
easeIn: (t: number) => t ** 1.675,
easeOut: (t: number) => 1 - (1 - t ** 1.675),
easeInOut: (t: number) => 0.5 * (Math.sin((t - 0.5) * Math.PI) + 1),
easeInQuad: (t: number) => t * t,
easeOutQuad: (t: number) => t * (2 - t),
easeInOutQuad: (t: number) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
easeInCubic: (t: number) => t * t * t,
easeOutCubic: (t: number) => (--t) * t * t + 1,
easeInOutCubic: (t: number) => (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),
easeInQuart: (t: number) => t * t * t * t,
easeOutQuart: (t: number) => 1 - (--t) * t * t * t,
easeInOutQuart: (t: number) => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t),
easeInQuint: (t: number) => t * t * t * t * t,
easeOutQuint: (t: number) => 1 + (--t) * t * t * t * t,
easeInOutQuint: (t: number) => (t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t),
};
export function animateNumber({
timing = timingFunctions.linear,
onUpdate,
duration,
onEnd,
from,
to,
}: AnimateNumberProps) {
const t0 = Date.now();
let canceled = false;
animate(() => {
if (canceled) return false;
const t1 = Date.now();
let t = (t1 - t0) / duration;
if (t > 1) t = 1;
const progress = timing(t);
if (typeof from === 'number' && typeof to === 'number') {
onUpdate(from + ((to - from) * progress));
} else if (Array.isArray(from) && Array.isArray(to)) {
const result = from.map((f, i) => f + ((to[i] - f) * progress));
onUpdate(result);
}
if (t === 1 && onEnd) onEnd();
return t < 1;
});
return () => {
canceled = true;
if (onEnd) onEnd();
};
}