Rocketmind

8. Анимации и Движение

Motion в Rocketmind — функциональный, не декоративный. Каждая анимация решает задачу: подтверждает действие, указывает направление, сообщает о смене состояния.

01
Минимализм
Анимировать только то, что несёт смысл. Без декора ради декора.
02
Скорость
100–300ms. Длинные анимации раздражают и замедляют восприятие.
03
Единообразие
Одни и те же easing-кривые и длительности по всей системе.

8.2 Timing-шкала

Полоска показывает относительную длину каждого токена.

--duration-instant50msНемедленная реакция (cursor, checkmark мгновенный)
--duration-fast100msHover-эффекты кнопок, смена цвета
--duration-base200msСтандарт: переходы состояний (active/disabled/focus)
--duration-smooth300msПоявление/скрытие элементов (dropdown, tooltip)
--duration-enter400msВходящие элементы (модал, панель)
--duration-grid1600msAnimated Grid Lines hero reveal — единственная длинная анимация

8.3 Easing-кривые

Нажми «Play», чтобы увидеть как шарик движется с данной кривой.

--ease-standard

Hover, focus, active — большинство переходов

cubic-bezier(0.4, 0, 0.2, 1)

--ease-enter

Появление элементов (модал, дропдаун, toast)

cubic-bezier(0, 0, 0.2, 1)

--ease-exit

Исчезновение элементов (закрытие, скрытие)

cubic-bezier(0.4, 0, 1, 1)

--ease-spring

Hover scale на карточках — небольшой перелёт

cubic-bezier(0.34, 1.56, 0.64, 1)

8.4 Микроинтерактивы

Наведи курсор на каждый элемент.

Button hover

Primary button: hover меняет цвет. 100ms ease-standard.

Ghost button hover

Ghost button использует прежний Secondary-style и на hover меняет серый фон. 100ms ease-standard.

Input focus

Input: при фокусе border жёлтый. 200ms ease-standard.

Agent card hover

Карточка агента: hover меняет border на фиолетовый. 200ms ease-spring.

AI-агент

Hover this card

Link CTA → arrow

Текстовая ссылка: hover сдвигает стрелку на 4px вправо. 100ms ease-standard.

Подробнее

Nav icon hover

Иконка в сайдбаре: hover меняет цвет с muted-foreground на foreground. 100ms ease-standard.

8.5 Переходы между состояниями экрана

Нажми кнопку, чтобы воспроизвести анимацию появления элемента.

Dropdown / Tooltip

Fade + slide 8px вниз. 300ms ease-enter.

Опции

Редактировать

Удалить

Модальное окно

Slide + scale от 0.98. 400ms ease-enter.

Диалог

Вы уверены в этом действии?

ДаНет

Toast / Notification

Slide справа-налево. 300ms ease-enter.

Изменения сохранены

Сообщение в чате

Fade + slide 8px снизу. 300ms ease-enter.

AI-агент

Привет! Чем могу помочь?

8.6 Loading / Skeleton

Skeleton занимает место сразу — нет «прыжков» при загрузке. Shimmer движется бесконечно.

Skeleton shimmer

Блоки-заглушки с движущимся блеском. Показываются пока грузятся данные.

Typing indicator

Три точки с stagger 0.2s. Показывает, что агент печатает ответ.

агент печатает...
8.7 Page-level правила
✓ Допустимо

Fade-in hero-контента: opacity 0→1, 400ms, ease-enter

Grid Lines reveal при загрузке (1600ms)

Skeleton placeholder до загрузки данных

Typing indicator в чате агента

Hover-переходы компонентов (100–200ms)

✗ Запрещено

Parallax-scrolling

Scroll-triggered transforms/fade

Page transitions с морфингом

Бесконечные фоновые анимации (pulse, float, orbit)

Particle systems

8.8 Доступность (Reduced Motion)

prefers-reduced-motion: reduce

Все анимации обязаны уважать системные настройки. Исключение — typing-indicator: заменяется на статичный •••.

8.9 Animated Grid Lines

Тонкие линии hero-секции материализуют каркас дизайна. Только одноразовая анимация при загрузке — после появления статичны. Используется с токеном --duration-grid (1600ms).

Hero background grid

scaleX/scaleY 0→1 · 1600ms ease-out · stagger 0.1s между линиями

8.10 Dot Grid Lens

Фоновый эффект «линзы» на сетке точек: при движении курсора точки вблизи него увеличиваются по квадратичному закону. Используется в hero-секции и CTA лендинга. Реализован через Canvas.

Двигай курсор
Токены, алгоритм и применение
--dot-size3pxБазовый диаметр точки
--dot-size-max10pxМаксимальный диаметр в центре линзы
--dot-gap20pxШаг сетки (расстояние между центрами)
--lens-radius120pxРадиус влияния курсора
--dot-color#CBCBCB / #404040Цвет точек (= --border токен)
--dot-color-accent#FFCC00Акцентный цвет точек в центре линзы

distance = sqrt((x − mx)² + (y − my)²)

t = clamp(1 − distance / LENS_RADIUS, 0, 1)

scale = 1 + (MAX_SCALE − 1) × t² // квадратичный easing

dotRadius = BASE_RADIUS × scale

ЭкранСекцияВариант
Лендинг — HeroПолный фон heroАкцентный (#FFCC00 в линзе)
Лендинг — CTAФон CTA-блокаМонохромный
AuthФоновый декорМонохромный, opacity: 0.5
Main AppНе используется

8.11 Hero Background Noise & Bottom Fade

Для тёмного hero допустим отдельный декоративный слой: мягкая подложка на токенах, статичный шум и нижний fade в --background. Эффект применяется только к фону, а не к контенту.

Noise + Fade
Без шума
VS
Токены и правила
Base backdrop--background + --rm-yellow-100 + --rm-gray-alpha-1002 radial-gradient и 1 vertical linear-gradient без новых цветов
Noise overlaysoft-light · opacity 0.04-0.06Только статичный SVG noise, без анимации
Bottom fadetransparent 74% -> var(--background) 100%Fade затрагивает только декоративный слой

Используем только в full-bleed hero на тёмном фоне. Шум всегда неинтерактивный и статичный, fade уходит в токен страницы, а логотип, copy и CTA остаются поверх слоя с полным контрастом.

8.12 Hero Round Glass Lens

Круглая hero-линза искажает уже собранную сцену внутри круга через WebGL, без blur и без отдельной DOM-копии контента внутри линзы. Центр остаётся почти стабильным, а fisheye/barrel-искажение усиливается к краю.

RocketmindДвигай курсор
Примеры использования

Пример 1 — anchorRef + motion

Rocketmind
<RoundGlassLens
  sceneRef={sceneRef}
  anchorRef={anchorRef}
  size={280}
  xOffset={60}
  yOffset={18}
  motionParallax
/>

Пример 2 — Custom optical settings

AI AGENTS

Powered by Rocketmind

<RoundGlassLens
  sceneRef={sceneRef}
  size={220}
  xOffset={-40}
  refraction={0.04}
  depth={0.22}
  dispersion={0.55}
  distortionRadius={1.1}
  blur={0.22}
  gradientAngle={135}
  motionParallax
/>

Пример 3 — Subtle / no shadow

RocketmindRocketmind

Strategic growth platform

<RoundGlassLens
  sceneRef={sceneRef}
  size={180}
  refraction={0.025}
  depth={0.14}
  dispersion={0.28}
  blur={0.12}
  shadowEnabled={false}
  motionParallax
/>
Props
PropTypeDefaultDescription
sceneRefRefObject<HTMLElement>Элемент для захвата (обязательно)
sizenumber320Диаметр линзы в px
x / ynumbercenterЦентр в координатах sceneRef
anchorRefRefObject<HTMLElement>Привязка к элементу (приоритет над x/y)
xOffset / yOffsetnumber0Смещение от центра анкора или сцены
refractionnumber0.03Сила преломления (0–0.12)
depthnumber0.18Глубина кривизны (0–0.5)
dispersionnumber0.36Хроматическая дисперсия (0–5)
distortionRadiusnumber1.08Радиус зоны искажения (0.2–1.5)
shadowRadiusnumber0.98Радиус краевой тени (0.2–1.5)
blurnumber0.18Радиальный блур (0–2)
gradientAnglenumber205Угол градиента ободка (0–360°)
shadowEnabledbooleantrueКраевое затемнение
motionParallaxbooleanautoПараллакс по курсору
motionStrengthnumber0.032Сила параллакса
showControlsbooleanfalseПоказать панель настройки (dev)
storageKeystringКлюч localStorage для сохранения настроек
Dev-воркфлоу
  1. 1Добавьте showControls storageKey="my-page:lens" в компонент.
  2. 2Настраивайте параметры в плавающей панели. Нажмите COPY — в буфере обмена JSX-пропсы.
  3. 3Вставьте пропсы в код, уберите showControls storageKey.
Токены, алгоритм и правила
Diameterclamp(280px, 30vw, 360px)Размер линзы в hero
Outer stroke1px linear-gradient(62deg, #FFE900 1%, #A6A6A6 40%, rgba(64,64,64,0) 100%)Тонкая градиентная обводка по кругу
Side flaresblue-white chromatic accentsКороткие световые вспышки слева и справа
Inner strokenoneВнутренняя 1px-линия не используется
Pointer offset limit64pxМаксимальное смещение от базовой точки
RendererWebGL + scene capture textureНе дублировать контент внутри линзы

rim = smoothstep(0.34, 0.98, normalized)

rimBand = rim × (1 − smoothstep(0.82, 1.0, normalized))

sampleOffset = localOffset × (1 − 0.22 × rimBand)

sampleOffset −= direction × radius × 0.085 × normalized³

Не вставляем внутрь линзы копию wordmark, меню, логотипов или background-слоёв. На pointer: coarse линза фиксируется на базовой позиции, а при prefers-reduced-motion: reduce отключается следование за курсором и дополнительные обновления сцены.

Secondary Lens

Допустима вторая большая линза справа без искажения сцены. Она использует тот же градиентный бордер и двигается за курсором в 3 раза медленнее и с амплитудой в 3 раза меньше, чем основная.

Доступность & Touch

На touch-устройствах (pointer: coarse) интерактивные hero-линзы фиксируются или отключаются, а декоративные фоны остаются статичными. При prefers-reduced-motion: reduce следование за курсором и дополнительные анимации останавливаются.

8.13 Wave Animation

Фоновый шейдер из бегущей сетки точек. Применяется в футере лендинга и на экране 404 как декоративный слой. Точки плавно затухают по мере удаления от камеры (глубина u_fade_near → u_fade_far), поэтому нижние точки остаются яркими, а верх плавно растворяется в фоне.

Параметры
pointSize3Размер точки в пикселях
waveSpeed2Скорость бегущей волны
waveIntensity10Частота синусоиды по сетке
particleColor#ffffffЦвет точек (применяем с альфой через fade)
gridDistance4Шаг сетки в мировых единицах
fadeNear20Глубина, с которой начинается затухание
fadeFar200Глубина, на которой точка полностью прозрачна
<WaveAnimation
  pointSize={3}
  waveSpeed={2}
  waveIntensity={10}
  particleColor="#ffffff"
  gridDistance={4}
/>

8.14 New-notification state («не просмотрено»)

Состояние для свежесозданной карточки/строки, которую пользователь ещё не открыл. Мягкая бледно-жёлтая подложка и плавно-пульсирующая жёлтая обводка — привлекают внимание без агрессии. Снимается после первого клика.

Новый проект

Подложка `--rm-yellow-10`, пульс 1.8s

Обычная карточка

Для сравнения — без пульса

Токены и параметры
bgvar(--rm-yellow-10)Подложка — самый бледный жёлтый из §1.1
shadow (peak)0 0 0 3px rgba(255, 204, 0, 0.55)Яркий пик пульса
shadow (idle)0 0 0 0 rgba(255, 204, 0, 0)Исходная прозрачность
duration1.8sПолный цикл пульса — idle → peak → idle
easingease-in-outПлавное нарастание и затухание
reduced-motionstatic 2px @ 0.4αСтатичная обводка при prefers-reduced-motion
<Link
  data-new={isNew || undefined}
  className="rounded-sm border border-border transition-colors
             data-[new=true]:bg-[var(--rm-yellow-10)]
             data-[new=true]:rm-new-pulse"
>
  …
</Link>

Правила использования: только на одном иерархическом уровне; не более 3 непросмотренных в списке; не сочетать с акцентной hover-обводкой. См. §8.14 DS MD.

8.15 Message rise (real-time-only)

Анимация «подлёта» новых сообщений в чате при отправке в real-time. Не применяется к исторически загруженным сообщениям. Визуально — bubble подлетает снизу (14px) через прозрачность за 0.28s.

Привет! Я помогу собрать маркет-бриф.
Параметры
fromopacity: 0; translateY(14px)Старт — лёгкое смещение снизу
toopacity: 1; translateY(0)Конечное положение bubble'а
duration0.28sБыстро, но заметно
easingcubic-bezier(0.2, 0.8, 0.2, 1)Ease-out — мягкое приземление
reduced-motionanimation: noneБез анимации для prefers-reduced-motion
// Set<string> id-шников real-time сообщений
const [freshIds, setFreshIds] = useState(new Set());

// При добавлении нового сообщения:
setMessages(p => [...p, msg]);
setFreshIds(p => new Set(p).add(msg.id));

// В bubble:
<div className={freshIds.has(msg.id) ? "rm-message-rise" : ""}>
  …
</div>

Правила: не применять при загрузке истории; один раз при mount (не повторяется); не комбинировать с `rm-new-pulse`. См. §8.15 DS MD.

8.16 Pipeline step dimmed state

Визуальное приглушение не-пройденных шагов последовательного процесса — в списках вроде sidebar R-экспертов. Пользователь мгновенно видит, что за спиной, что сейчас, что впереди.

R-команда · 2 из 6
R1Роман✓ Завершена
R2Регина✓ Завершена
R+Роза◌ Ждёт валидации
R3Римма○ Не начата
R4Роберт○ Не начата
R5Рон○ Не начата
Матрица состояний
completedopacity: 100%Нормальная яркость, CheckCircle2 в жёлтом акценте
in_progress / awaiting_validationopacity: 100%Нормальная яркость, CircleDashed в foreground
current (у проекта) + not_startedopacity: 100%Текущий следующий шаг — не затемнять никогда
not_started (не current)opacity: 50%Приглушён, сигнал «впереди»
hoveropacity: 100%Восстанавливается на hover
const isDimmed =
  status === "not_started" &&
  currentExpertCodename !== codename;

<div className={`… ${isDimmed ? "opacity-50 hover:opacity-100" : ""}`}>
  …
</div>

Правила: current-шаг никогда не приглушается; hover восстанавливает; не использовать в dashboard-сценариях (там прогресс-бар). См. §8.16 DS MD.

8.17 Content load — fade-in-up

Появление контента при загрузке экрана в SaaS-обучении: тело урока, дашборд программы («разводная»), вопрос блиц-теста, экран AI-эксперта, профиль. opacity 0→1 + подъём 8px, 400ms, ease-enter — одним блоком.

Урок · Введение

Контент появляется через прозрачность и лёгкий подъём 8px. Один раз при загрузке экрана — урок, дашборд программы, вопрос блиц-теста, чат агента, профиль.

Параметры
fromopacity: 0; translateY(8px)Старт — лёгкое смещение снизу
toopacity: 1; translateY(0)Конечное положение контента
durationvar(--duration-enter) = 400msПоявление экрана
easingvar(--ease-enter)Decelerate — мягкое торможение
reduced-motionanimation: noneБез анимации для prefers-reduced-motion
// Класс .rm-content-enter в @rocketmind/ui globals.css.
// Перезапуск анимации на смене контента — через React key:
<article key={lesson.id} className="rm-content-enter …">…</article>

Правила: один раз на маунт контейнера; перезапуск через key (id урока/программы/индекс вопроса/id агента); НЕ scroll-triggered; НЕ на каждый элемент списка. См. §8.17 DS MD.