Animations
Минималистичный движок таймлайн-анимаций. Один HTML, React + inline JSX, без сторонних либ кроме Babel.
Что даёт templates/anim-engine.jsx
<Stage duration={...}> — корневой контейнер. Авто-скейлит канвас под viewport, рисует скраббер и play/pause.
<Sprite start={s} end={s} ...> — обёртка с временными границами. Внутри читает useTime() и рендерит детей.
useTime() — текущая позиция таймлайна в секундах.
useSprite() — нормализованная позиция внутри спрайта (0..1).
Easing — linear, easeIn, easeOut, easeInOut, expo, back, bounce.
interpolate(t, [from, to], easing) — линейная интерполяция чисел или массивов чисел (для transform).
entryFade, entryRise, exitFade — готовые входы/выходы.
Структура сцены
<Stage duration={8} width={1920} height={1080}>
<Sprite start={0} end={3}>
{() => {
const p = useSprite();
const y = interpolate(p, [40, 0], Easing.easeOut);
const o = interpolate(p, [0, 1], Easing.easeOut);
return <h1 style={{ transform: `translateY(${y}px)`, opacity: o }}>Hello</h1>;
}}
</Sprite>
<Sprite start={2} end={5}>
{() => <SecondScene />}
</Sprite>
</Stage>
Принципы
- Всё происходит в долях секунды. Не в кадрах. 24/30/60 fps — забота браузера.
- Перекрытия — благо. Последние 0.3s одного спрайта пересекаются с первыми 0.3s следующего — глаз не цепляется за стыки.
- Easing — не декорация.
easeOut для входов («падает и тормозит»), easeIn для выходов («ускоряется и улетает»), easeInOut для одновременных движений, linear для камер и пэннингов.
- Не анимируй всё. Один-два элемента в кадре движутся, остальные стоят. Иначе каша.
- Сохраняй позицию плеера. Если делаешь длинную анимацию для итераций — пиши текущее время в
localStorage, чтоб рефреш не сбрасывал.
Экспорт в видео
Если попросят MP4/GIF, дёргай export-png для покадрового рендера через Playwright + ffmpeg для склейки.
Что НЕ делать
- Не имитируй keyframes-CSS через JS-таймауты. Используй
requestAnimationFrame (он внутри Stage).
- Не используй GSAP / Framer Motion / Popmotion, если задачу решает встроенный движок. Бандл-вес и сложность не оправданы.
- Не пытайся синхронизировать анимацию со звуком через
setTimeout. Используй audio.currentTime как источник правды.
1---2name: animations3description: Таймлайн-анимации в HTML (React): плеер, скраббер, ease. Триггеры: «анимация в HTML», «motion design», «интро», «transitions для презентации».4---56# Animations78Минималистичный движок таймлайн-анимаций. Один HTML, React + inline JSX, без сторонних либ кроме Babel.910## Что даёт `templates/anim-engine.jsx`1112- `<Stage duration={...}>` — корневой контейнер. Авто-скейлит канвас под viewport, рисует скраббер и play/pause.13- `<Sprite start={s} end={s} ...>` — обёртка с временными границами. Внутри читает `useTime()` и рендерит детей.14- `useTime()` — текущая позиция таймлайна в секундах.15- `useSprite()` — нормализованная позиция внутри спрайта (0..1).16- `Easing` — `linear`, `easeIn`, `easeOut`, `easeInOut`, `expo`, `back`, `bounce`.17- `interpolate(t, [from, to], easing)` — линейная интерполяция чисел или массивов чисел (для transform).18- `entryFade`, `entryRise`, `exitFade` — готовые входы/выходы.1920## Структура сцены2122```jsx23<Stage duration={8} width={1920} height={1080}>24 <Sprite start={0} end={3}>25 {() => {26 const p = useSprite();27 const y = interpolate(p, [40, 0], Easing.easeOut);28 const o = interpolate(p, [0, 1], Easing.easeOut);29 return <h1 style={{ transform: `translateY(${y}px)`, opacity: o }}>Hello</h1>;30 }}31 </Sprite>3233 <Sprite start={2} end={5}>34 {() => <SecondScene />}35 </Sprite>36</Stage>37```3839## Принципы4041- **Всё происходит в долях секунды.** Не в кадрах. 24/30/60 fps — забота браузера.42- **Перекрытия — благо.** Последние 0.3s одного спрайта пересекаются с первыми 0.3s следующего — глаз не цепляется за стыки.43- **Easing — не декорация.** `easeOut` для входов («падает и тормозит»), `easeIn` для выходов («ускоряется и улетает»), `easeInOut` для одновременных движений, `linear` для камер и пэннингов.44- **Не анимируй всё.** Один-два элемента в кадре движутся, остальные стоят. Иначе каша.45- **Сохраняй позицию плеера.** Если делаешь длинную анимацию для итераций — пиши текущее время в `localStorage`, чтоб рефреш не сбрасывал.4647## Экспорт в видео4849Если попросят MP4/GIF, дёргай `export-png` для покадрового рендера через Playwright + ffmpeg для склейки.5051## Что НЕ делать5253- Не имитируй keyframes-CSS через JS-таймауты. Используй `requestAnimationFrame` (он внутри Stage).54- Не используй GSAP / Framer Motion / Popmotion, если задачу решает встроенный движок. Бандл-вес и сложность не оправданы.55- Не пытайся синхронизировать анимацию со звуком через `setTimeout`. Используй `audio.currentTime` как источник правды.