import { canUseDOM, canUseEventListeners } from '../other/dom.ts'; import { type SupportEvent } from '../other/types.ts'; type TimingInterface = (timeFraction: number) => number; type DrawInterface = (progress: number) => void; interface AnimateArgumentsInterface { /** * Длительность */ duration: number; /** * Тайминг функция анимации */ timing: TimingInterface; /** * Коллбэк, в который прокидывается прогресс [0, 1] */ draw: DrawInterface; } /** * Функция для js анимации */ export function animate({ duration, timing, draw }: AnimateArgumentsInterface): void { if (!canUseDOM) { return; } const start = window.performance.now(); // eslint-disable-next-line no-shadow window.requestAnimationFrame(function animate(time) { let timeFraction = (time - start) / duration; if (timeFraction > 1) { timeFraction = 1; } const progress = timing(timeFraction); draw(progress); if (timeFraction < 1) { window.requestAnimationFrame(animate); } }); } // WebKitAnimationEvent и WebKitTransitionEvent не существуют в глобальном контексте declare const WebKitAnimationEvent: AnimationEvent; declare const WebKitTransitionEvent: TransitionEvent; export const animationEvent: SupportEvent<'animationend'> = /*#__PURE__*/ (() => { const obj: SupportEvent<'animationend'> = { supported: false, name: 'animationend', }; if (canUseDOM) { if (typeof AnimationEvent !== 'undefined') { obj.supported = true; } else if (typeof WebKitAnimationEvent !== 'undefined') { obj.supported = true; // webkitAnimationEnd не входит в перечисление событий, но соответствует animationend obj.name = 'webkitAnimationEnd' as unknown as 'animationend'; } } return obj; })(); export const transitionEvent: SupportEvent<'transitionend'> = /*#__PURE__*/ (() => { const obj: SupportEvent<'transitionend'> = { supported: false, name: 'transitionend', }; if (canUseDOM) { if (typeof TransitionEvent !== 'undefined') { obj.supported = true; } else if (typeof WebKitTransitionEvent !== 'undefined') { obj.supported = true; // webkitTransitionEnd не входит в перечисление событий, но соответствует transitionend obj.name = 'webkitTransitionEnd' as unknown as 'transitionend'; } } return obj; })(); /** * Ожидание окончания анимации на элементе * * @param listener Коллбэк окончания ожидания * @param fallbackTime Сколько ждать в мс если событие не поддерживается * @param el Элемент */ export function waitAnimationEnd( listener: (ev?: AnimationEvent) => any, fallbackTime: number, el?: GlobalEventHandlers, ): number | undefined { if (canUseEventListeners) { if (animationEvent.supported && el) { el.addEventListener(animationEvent.name, listener); } else { return window.setTimeout(listener, fallbackTime); } } } /** * Прекращение ожидания окончания анимации на элементе * * @param listener Коллбэк окончания ожидания * @param handle то, что вернулось из ```waitAnimationEnd``` * @param el Элемент */ export function cancelWaitAnimationEnd( listener: (ev?: AnimationEvent) => any, handle?: number, el?: GlobalEventHandlers, ): void { if (canUseEventListeners) { if (animationEvent.supported && el) { el.removeEventListener(animationEvent.name, listener); } else { window.clearTimeout(handle); } } } /** * Ожидание окончания анимации перехода на элементе * * @param listener Коллбэк окончания ожидания * @param fallbackTime Сколько ждать в мс если событие не поддерживается * @param el Элемент */ export function waitTransitionEnd( el: GlobalEventHandlers, listener: (ev?: TransitionEvent) => any, fallbackTime: number, ): number | undefined { if (canUseEventListeners) { if (transitionEvent.supported && el) { el.addEventListener(transitionEvent.name, listener); } else { return window.setTimeout(listener, fallbackTime); } } } /** * Прекращение ожидания окончания анимации перехода на элементе * * @param listener Коллбэк окончания ожидания * @param handle То, что вернулось из ```waitTransitionEnd``` * @param el Элемент */ export function cancelWaitTransitionEnd( listener: (ev?: TransitionEvent) => any, handle?: number, el?: GlobalEventHandlers, ): void { if (canUseEventListeners) { if (transitionEvent.supported && el) { el.removeEventListener(transitionEvent.name, listener); } else { window.clearTimeout(handle); } } }