Анимация
Простыми словами
Плавное движение: часы кадров и позиция как накопленная скорость — или просто CSS-переход, если хватает его.
Continuum наследует исходную идею Конала Эллиотта: анимация — не цикл, мутирующий пиксели, а уравнение над непрерывным временем. Позиция — интеграл скорости; скорость — производная позиции; перемотка — перекройка часов.
Часы
Непрерывное время в дискретном движке численно сэмплируется. Часы — это обычное событие таймстемпов:
import { animationFrames } from "@continuum-js/dom";
const ticks = animationFrames(); // Stream<number> таймстемпов rAF (мс)animationFrames() регистрируется у текущего владельца — когда поддерево компонента уничтожается, цикл requestAnimationFrame останавливается сам.
integral: позиция из скорости
import { constant, integral } from "@continuum-js/frp";
const velocity = constant(0.1); // px в мс
const x = integral(velocity, ticks); // Behavior<number>, px
const style = x.map((px) => ({ transform: `translateX(${px}px)` }));
return <div class="box" style={style} />;integral(b, tick, init?) накапливает b · dt раз за тик (прямой Эйлер). Описание не зависит от разрешения — «позиция есть интеграл скорости», — а разрешение часов появляется только в момент сэмплирования. Скорость — Behavior, значит сама может быть производной: ease-out'ы, пружины, пауза (скорость 0) — обычные map.
derivative: скорость из позиции
import { derivative } from "@continuum-js/frp";
const speed = derivative(mouseX, ticks); // px в мс, конечные разностиПригодится для скорости жеста (fling) или чтобы реагировать на темп изменения значения, а не на само значение.
warp: искривление времени
Искривлённые часы — это просто отображённое событие, и всё, что ниже по течению, живёт в искривлённом времени:
import { warp } from "@continuum-js/frp";
const fast = integral(
velocity,
warp(ticks, (t) => t * 2),
); // скорость 2×
const reverse = warp(ticks, (t) => -t); // время назадЗамедление, перемотка и пауза — преобразования часов, а не особые случаи внутри кода анимации.
Дискретная анимация
Не всему нужно непрерывное время. Спрайт по тикам — это свёртка:
const frame = ticks.accum(0, (_t, f) => (f + 1) % FRAMES);
const src = frame.map((f) => `/sprites/run-${f}.png`);CSS-переходы тоже никто не отменял: для простого движения «по состоянию» привяжите класс (class={open.map((o) => (o ? "panel open" : "panel"))}) и отдайте анимацию браузеру.
Работающий пример
Пример animation рендерит две коробки: одну на обычных часах, другую на warp(t => 2t) — тот же integral, вдвое большая дистанция за то же настенное время.
Незнакомый термин? Вся терминология этой документации объяснена в глоссарии.