useFrame
Описание
useFrame — хук, который вызывает ваш колбэк на каждом кадре анимации (requestAnimationFrame). Колбэк получает полезные тайминги:
frame— номер кадра, начиная с1;deltaTime— миллисекунды с предыдущего кадра;timeSinceStart— миллисекунды с первого кадра.
Хук удобен для анимаций, синхронизации с частотой обновления экрана, измерений и постепенных вычислений.
Сигнатура
ts
function useFrame(callback: UseFrameCallback): void;Параметры
callback— функция, вызываемая на каждомrequestAnimationFrameс объектом таймингов.
Возвращает
void— побочных значений нет; хук лишь подписывает/отписывает кадры.
Примеры
1) Логирование таймингов
tsx
import { useFrame } from '@webeach/react-hooks';
export function Logger() {
useFrame(({ frame, deltaTime, timeSinceStart }) => {
if (frame % 60 === 0) {
console.log(
'frame',
frame,
'Δ',
deltaTime.toFixed(2),
'ms',
'total',
timeSinceStart.toFixed(2),
'ms',
);
}
});
return null;
}2) Плавное перемещение (с использованием deltaTime)
tsx
import { useRef } from 'react';
import { useFrame } from '@webeach/react-hooks';
export function BoxMover() {
const boxRef = useRef<HTMLDivElement>(null);
const xRef = useRef(0);
useFrame(({ deltaTime }) => {
xRef.current += 0.1 * deltaTime; // 0.1px на миллисекунду (≈6px/кадр при 60fps)
if (boxRef.current) {
boxRef.current.style.transform = `translateX(${xRef.current}px)`;
}
});
return (
<div
ref={boxRef}
style={{ width: 40, height: 40, backgroundColor: 'green' }}
/>
);
}3) Пауза/возобновление анимации
tsx
import { useState } from 'react';
import { useFrame, useToggle } from '@webeach/react-hooks';
export function Clock() {
const [paused, togglePaused] = useToggle(false);
const [ms, setMs] = useState(0);
useFrame(({ deltaTime }) => {
if (paused) {
return;
}
setMs((prevMs) => prevMs + deltaTime);
});
return (
<div>
<output>{ms.toFixed(0)} ms</output>
<button onClick={() => togglePaused()}>
{paused ? 'Resume' : 'Pause'}
</button>
</div>
);
}Поведение
Частота вызовов
- Колбэк вызывается один раз на кадр браузерной анимации; частота зависит от устройства и вкладки.
Тайминги
- В
callbackприходятframe,deltaTimeиtimeSinceStart— можно строить кадрово‑независимые анимации.
- В
Актуальность логики
- Хук использует актуальную версию переданного
callback; обновление пропсов/состояния в компоненте автоматически подхватывается без ручной переподписки.
- Хук использует актуальную версию переданного
Очистка
- При размонтировании подписка снимается; следующий кадр отменяется корректно.
Layout‑контекст
- Эффект синхронизируется с фазой layout; это удобно для измерений и подготовки перед пейнтом.
Когда использовать
- Кадровые анимации и интерполяции (позиции, непрозрачности, счётчики).
- Плавные интеграции с Canvas/WebGL/SVG.
- Регулярные измерения/синхронизация состояния с реальным временем кадра.
Когда не использовать
- Для редких/разовых действий — достаточно
setTimeout/useEffect. - Для тяжёлых вычислений на каждом кадре, которые могут блокировать UI — распределяйте работу, уменьшайте нагрузку или снижайте частоту.
Частые ошибки
Тяжёлая работа в кадре
- Длительный код внутри колбэка увеличит
deltaTimeи сделает анимацию «дёрганой». Выносите тяжёлые задачи, используйте батчинг/воркеры.
- Длительный код внутри колбэка увеличит
Ожидание фиксированных 60fps
- Частота кадров не гарантирована; используйте
deltaTimeдля кадрово‑независимых расчётов.
- Частота кадров не гарантирована; используйте
Ссылочные мутации без рендера
- Если меняете DOM/рефы вручную, UI обновится; но если храните значения в обычных переменных, они не вызывают ререндер — используйте
useState/useRefпо задаче.
- Если меняете DOM/рефы вручную, UI обновится; но если храните значения в обычных переменных, они не вызывают ререндер — используйте
Типизация
Экспортируемые типы
UseFrameCallback(info: { frame: number; deltaTime: number; timeSinceStart: number }) => void— функция, вызываемая на каждом кадре с таймингами.