Морфинг фигур в интерфейсах строится на непрерывном преобразовании
одного векторного контура в другой. В вебе базовой единицей такого
преобразования выступает атрибут d элемента SVG
<path>. Он представляет собой строку команд,
описывающих кривые Безье, линии и дуги.
Проблема прямого морфинга заключается в несовместимости путей: два произвольных контура почти никогда не имеют одинаковую структуру команд и одинаковое количество точек. Поэтому перед анимацией требуется привести оба пути к общей параметрической форме.
Для корректного морфинга необходимо:
Эта задача решается через специализированные библиотеки интерполяции
путей. На практике часто используется flubber, которая
строит функцию перехода между двумя d-строками.
import { interpolate } from "flubber";
const fromPath = "M10,80 Q100,10 200,80 T390,80";
const toPath = "M10,10 200,10 200,200 10,200 Z";
const morph = interpolate(fromPath, toPath);
Функция morph(t) возвращает промежуточный путь для
значения t в диапазоне от 0 до 1.
Popmotion предоставляет универсальный механизм анимации значений
через animate. Его задача — управлять временем и выдавать
прогресс анимации, который затем используется для вычисления
промежуточного состояния SVG.
import { animate } from "popmotion";
Связка выглядит следующим образом:
import { animate } from "popmotion";
import { interpolate } from "flubber";
const fromPath = "M20,20 L180,20 L180,180 L20,180 Z";
const toPath = "M100,20 C180,20 180,180 100,180 C20,180 20,20 100,20 Z";
const morph = interpolate(fromPath, toPath);
const pathElement = document.querySelector("path");
animate({
from: 0,
to: 1,
duration: 1200,
onUpdate: (t) => {
pathElement.setAttribute("d", morph(t));
}
});
В этом примере Popmotion отвечает только за прогресс t.
Геометрия полностью контролируется функцией morph.
Popmotion позволяет подключать функции сглаживания, изменяющие характер движения параметра времени. Это напрямую влияет на визуальное ощущение морфинга.
import { animate, easeInOut } from "popmotion";
Применение easing:
animate({
from: 0,
to: 1,
duration: 1500,
ease: easeInOut,
onUpdate: (t) => {
pathElement.setAttribute("d", morph(t));
}
});
Разные кривые дают разные эффекты:
Popmotion поддерживает физические анимации, включая пружины. Это позволяет сделать переход между фигурами более «живым», особенно в интерактивных сценариях.
import { animate, spring } from "popmotion";
animate({
from: 0,
to: 1,
type: "spring",
stiffness: 120,
damping: 18,
onUpdate: (t) => {
pathElement.setAttribute("d", morph(t));
}
});
Физическая модель делает движение менее линейным и более реактивным к изменениям состояния.
Когда требуется переход через промежуточные формы, используется подход с несколькими ключевыми состояниями.
import { animate } from "popmotion";
import { interpolate } from "flubber";
const square = "M20,20 L180,20 L180,180 L20,180 Z";
const triangle = "M100,20 L180,180 L20,180 Z";
const circle = "M100,20 A80,80 0 1 1 99.9,20 Z";
const squareToTriangle = interpolate(square, triangle);
const triangleToCircle = interpolate(triangle, circle);
animate({
from: 0,
to: 2,
duration: 2400,
onUpdate: (t) => {
if (t < 1) {
pathElement.setAttribute("d", squareToTriangle(t));
} else {
pathElement.setAttribute("d", triangleToCircle(t - 1));
}
}
});
Такой подход позволяет строить сложные морфинговые цепочки без потери контроля над формой.
Морфинг часто комбинируется с трансформациями scale,
rotate, translate. Popmotion позволяет
анимировать несколько свойств одновременно через независимые
анимационные процессы.
import { animate } from "popmotion";
animate({
from: 0,
to: 1,
duration: 1200,
onUpdate: (t) => {
pathElement.setAttribute("d", morph(t));
pathElement.style.transform = `scale(${1 + t * 0.3}) rotate(${t * 90}deg)`;
}
});
При таком подходе важно учитывать, что трансформации применяются
поверх геометрии пути и не влияют на сам d.
Изменение формы часто сопровождается изменением цвета, чтобы усилить визуальную непрерывность перехода.
import { animate, mixColor } from "popmotion";
const fromColor = "#ff6a00";
const toColor = "#4a90e2";
animate({
from: 0,
to: 1,
duration: 1200,
onUpdate: (t) => {
pathElement.setAttribute("d", morph(t));
pathElement.style.fill = mixColor(fromColor, toColor)(t);
}
});
Цветовая интерполяция уменьшает визуальный разрыв между несовместимыми геометриями.
Морфинг становится более выразительным при привязке к событиям интерфейса: наведению, клику или движению курсора.
let current = 0;
element.addEventListener("mouseenter", () => {
animate({
from: current,
to: 1,
duration: 600,
onUpdate: (t) => {
current = t;
pathElement.setAttribute("d", morph(t));
}
});
});
element.addEventListener("mouseleave", () => {
animate({
from: current,
to: 0,
duration: 600,
onUpdate: (t) => {
current = t;
pathElement.setAttribute("d", morph(t));
}
});
});
Такой подход требует хранения текущего состояния анимации, чтобы новые события не «обнуляли» переход.
В сложных интерфейсах целевая форма может меняться во время анимации. Тогда интерполяция пересоздаётся на лету.
let morph = interpolate(pathA, pathB);
let progress = 0;
function setTarget(newPath) {
morph = interpolate(pathB, newPath);
pathB = newPath;
}
animate({
from: 0,
to: 1,
repeat: Infinity,
onUpdate: (t) => {
progress = t;
pathElement.setAttribute("d", morph(t));
}
});
Это позволяет реализовать адаптивные морфинговые системы, где фигуры «перетекают» в новые состояния без перезапуска анимации.
При высокой частоте обновления важно минимизировать нагрузку:
requestAnimationFrame внутри Popmotion
(по умолчанию уже применяется)Снижение количества вычислений особенно критично при одновременном морфинге нескольких объектов.
Хотя SVG остаётся базовым инструментом, Popmotion может управлять и Canvas-отрисовкой через ту же модель интерполяции.
animate({
from: 0,
to: 1,
duration: 1000,
onUpdate: (t) => {
const path = morph(t);
ctx.clearRect(0, 0, canvas.width, canvas.height);
const shape = new Path2D(path);
ctx.fill(shape);
}
});
Такой подход полезен при большом количестве объектов или сложных сценах, где SVG становится узким местом.
Морфинг не ограничивается геометрически похожими фигурами. При корректной интерполяции возможно преобразование:
Ключевым фактором остаётся подготовка совместимых путей, где структура кривых позволяет построить непрерывное преобразование без разрывов и скачков.