Path анимации

Path-анимация в контексте Popmotion строится вокруг идеи движения объекта вдоль заранее заданной кривой. В веб-разработке такие кривые чаще всего представлены SVG-путями (<path>), которые описывают сложные траектории через команды линий, кривых Безье и дуг.

Ключевая особенность SVG-пути заключается в том, что он одновременно является и визуальным элементом, и математическим описанием траектории. Браузер предоставляет API для работы с такими траекториями:

  • getTotalLength() — возвращает общую длину пути
  • getPointAtLength(distance) — возвращает координаты точки на пути по смещению

Именно эта модель становится базой для построения path-анимаций: вместо работы с уравнениями кривых используется параметр длины пути.

Popmotion не навязывает готовую систему path-follow, но предоставляет инструменты (tween, value, spring, physics), которые позволяют построить такую систему вручную через композицию значений.


Преобразование прогресса в движение по траектории

Любая path-анимация сводится к преобразованию нормализованного прогресса t ∈ [0, 1] в координаты на пути.

Алгоритм:

  1. Получить SVG-path

  2. Вычислить его длину

  3. На каждом шаге анимации вычислять позицию:

    • distance = length * t
    • point = path.getPointAtLength(distance)

Таким образом, траектория становится функцией отображения:

t → (x, y)

Базовая реализация движения объекта по пути

Основой выступает value из Popmotion, который позволяет хранить и обновлять состояние анимации в реальном времени.

import { value, tween } from 'popmotion';

const path = document.querySelector('path');
const box = document.querySelector('.box');

const length = path.getTotalLength();

const motion = value(0, (v) => {
  const point = path.getPointAtLength(v * length);

  box.style.transform = `translate(${point.x}px, ${point.y}px)`;
});

tween({
  from: 0,
  to: 1,
  duration: 2000,
}).start(motion);

В данной схеме:

  • tween отвечает за временную интерполяцию
  • value обрабатывает каждый кадр
  • SVG path используется как функция координат

Управление прогрессом через tween

tween в Popmotion представляет собой детерминированную анимацию, основанную на времени. Это делает path-анимацию воспроизводимой и легко синхронизируемой.

Важные параметры:

  • duration — длительность движения
  • ease — функция сглаживания
  • loop — зацикливание
  • flip — обратное воспроизведение

Пример нелинейного движения:

import { tween, easing } from 'popmotion';

tween({
  from: 0,
  to: 1,
  duration: 3000,
  ease: easing.easeInOut,
}).start((v) => {
  const point = path.getPointAtLength(v * length);

  box.style.transform = `translate(${point.x}px, ${point.y}px)`;
});

Использование easing влияет не на геометрию пути, а на распределение времени вдоль него. Это позволяет создавать эффекты ускорения на прямых участках и замедления на поворотах без изменения самой траектории.


Поворот объекта по направлению движения

Для реалистичной path-анимации важно учитывать не только позицию, но и ориентацию объекта.

Направление вычисляется через разницу двух близких точек на пути:

const delta = 0.001;

const p1 = path.getPointAtLength(t * length);
const p2 = path.getPointAtLength((t + delta) * length);

const angle = Math.atan2(p2.y - p1.y, p2.x - p1.x);

Применение:

box.style.transform = `
  translate(${p1.x}px, ${p1.y}px)
  rotate(${angle}rad)
`;

Такой подход позволяет объекту «смотреть» по направлению движения, что критично для симуляции транспорта, частиц или персонажей.


Синхронизация нескольких объектов на одном пути

Path-анимация часто используется для распределения множества элементов вдоль одной траектории.

Подход основан на смещении прогресса:

const items = Array.from(document.querySelectorAll('.item'));

tween({
  from: 0,
  to: 1,
  duration: 4000,
}).start((v) => {
  items.forEach((item, i) => {
    const offset = i * 0.05;
    const t = (v + offset) % 1;

    const point = path.getPointAtLength(t * length);

    item.style.transform = `translate(${point.x}px, ${point.y}px)`;
  });
});

Так формируется эффект потока объектов, распределённых по одной траектории.


Управление движением через spring и physics

Вместо tween можно использовать физические модели Popmotion, такие как spring и physics.

Spring-модель:

import { spring, value } from 'popmotion';

const motion = value(0, (v) => {
  const point = path.getPointAtLength(v * length);
  box.style.transform = `translate(${point.x}px, ${point.y}px)`;
});

spring({
  from: 0,
  to: 1,
  stiffness: 80,
  damping: 20,
}).start(motion);

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

Physics-модель позволяет управлять скоростью напрямую:

import { physics, value } from 'popmotion';

physics({
  velocity: 0.2,
  friction: 0.15,
}).start((v) => {
  const t = v % 1;

  const point = path.getPointAtLength(t * length);
  box.style.transform = `translate(${point.x}px, ${point.y}px)`;
});

Работа с нелинейными траекториями и сегментацией пути

SVG path может содержать сложные кривые, где равномерное движение по длине визуально выглядит неравномерным. Для компенсации применяются техники ресэмплинга:

  • разбиение пути на N сегментов
  • предварительное кэширование точек
  • использование lookup-таблиц

Пример предвычисления:

const samples = 200;
const points = [];

for (let i = 0; i <= samples; i++) {
  const t = i / samples;
  const point = path.getPointAtLength(t * length);

  points.push(point);
}

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


Комбинирование path с transform-системой Popmotion

Popmotion предоставляет трансформационные утилиты, которые позволяют расширять path-анимацию:

  • scale
  • rotate
  • translate
  • compose

Пример комбинирования:

const update = (x, y, angle) => {
  box.style.transform = `
    translate(${x}px, ${y}px)
    rotate(${angle}rad)
    scale(1.2)
  `;
};

Path становится источником базовой геометрии, а transform-цепочка отвечает за визуальную модификацию.


Оптимизация path-анимаций

При интенсивной анимации важно учитывать производительность:

  • минимизация вызовов getPointAtLength
  • использование requestAnimationFrame (Popmotion делает это автоматически)
  • кэширование длины пути
  • ограничение числа объектов на одной траектории
  • переход на precomputed points при сложных сценах

Дополнительно снижение нагрузки достигается переносом вычислений в отдельные структуры данных и уменьшением DOM-операций.


Типовые архитектурные паттерны

Path как источник данных

SVG path рассматривается не как визуальный элемент, а как математическая функция.

Разделение времени и пространства

  • tween/spring управляют временем
  • path управляет координатами

Потоковая модель

Каждый кадр — это функция:

state(t) → position → transform

Инстансная модель объектов

Каждый объект имеет собственный offset, но общий path и timeline.


Многослойные path-системы

Сложные сцены используют несколько уровней траекторий:

  • базовый path (основное движение)
  • secondary path (дрожание, шум)
  • micro-offset (физические отклонения)

Комбинация этих уровней позволяет создавать органичное движение без ручной анимации ключевых кадров.


Управление скоростью вдоль пути

Хотя стандартный подход предполагает равномерное распределение по длине, скорость можно модулировать через ремаппинг прогресса:

  • ускорение на прямых
  • замедление на кривых
  • динамическая деформация пути

Это достигается через кастомные функции интерполяции между t и distance, заменяющие линейное масштабирование.