Интерполяция между путями

В Two.js объекты Path используются для построения сложных форм с помощью линий и кривых. Каждый путь состоит из массива вершин (vertices), которые задают ключевые точки фигуры. Основные типы вершин:

  • Two.Anchor – определяет точку с координатами (x, y) и опциональными управляющими кривыми controlLeft и controlRight.
  • Path может быть замкнутым (closed) или открытым.

Пример создания пути:

const path = two.makePath(
  50, 50,
  150, 50,
  150, 150,
  50, 150,
  true // замкнутый путь
);
path.stroke = '#ff0000';
path.linewidth = 2;

Здесь вершины формируют квадрат. Параметр true закрывает путь, соединяя последнюю вершину с первой.


Интерполяция между путями

Интерполяция позволяет плавно изменять одну форму в другую. Для этого важно, чтобы:

  1. Количество вершин совпадало между исходным и целевым путями.
  2. Типы вершин соответствовали: линии должны сочетаться с линиями, кривые с кривыми.
  3. Вершины упорядочены одинаково, иначе анимация будет “ломанной”.

Создание интерполяции выполняется с помощью метода vertices и циклического обновления координат:

const pathA = two.makePath(50,50, 150,50, 150,150, 50,150, true);
const pathB = two.makePath(100,100, 200,50, 200,200, 100,250, true);

two.bind('upd ate', function(frameCount) {
  const t = (Math.sin(frameCount * 0.05) + 1) / 2; // значение от 0 до 1
  for (let i = 0; i < pathA.vertices.length; i++) {
    pathA.vertices[i].x = pathA.vertices[i].x * (1 - t) + pathB.vertices[i].x * t;
    pathA.vertices[i].y = pathA.vertices[i].y * (1 - t) + pathB.vertices[i].y * t;
  }
}).play();

Здесь используется линейная интерполяция (LERP) для координат каждой вершины. Параметр t изменяется во времени, создавая плавное превращение одной формы в другую.


Интерполяция кривых Безье

Если путь содержит кривые, нужно учитывать управляющие точки controlLeft и controlRight. Для плавной трансформации кривых они интерполируются так же, как и основные координаты вершины:

pathA.vertices[i].controlLeft.x = pathA.vertices[i].controlLeft.x * (1 - t) + pathB.vertices[i].controlLeft.x * t;
pathA.vertices[i].controlLeft.y = pathA.vertices[i].controlLeft.y * (1 - t) + pathB.vertices[i].controlLeft.y * t;

pathA.vertices[i].controlRight.x = pathA.vertices[i].controlRight.x * (1 - t) + pathB.vertices[i].controlRight.x * t;
pathA.vertices[i].controlRight.y = pathA.vertices[i].controlRight.y * (1 - t) + pathB.vertices[i].controlRight.y * t;

Это позволяет кривым плавно деформироваться вместе с изменением основной формы.


Масштабирование и смещение путей перед интерполяцией

Перед анимацией часто требуется унифицировать размеры и положение обеих фигур, чтобы трансформация выглядела корректно. Используются свойства translation, scale, rotation:

pathA.translation.se t(100, 100);
pathB.translation.set(100, 100);

pathA.scale = 1;
pathB.scale = 1;

Для сложных анимаций можно использовать Two.Group для объединения нескольких путей и применения трансформаций к группе целиком.


Контроль скорости и плавности

  • Функции easing позволяют управлять скоростью интерполяции. В Two.js можно применять сторонние библиотеки или реализовать собственные функции на основе Math.sin, Math.cos, Math.pow.
  • Фреймрейт влияет на гладкость: рекомендуется использовать 60 FPS.
  • Для циклических анимаций удобно задавать t = (Math.sin(frameCount * factor) + 1) / 2.

Создание сложных морфингов

  1. Нормализация путей – добавление или удаление вершин, чтобы количество совпадало.
  2. Учет кривизны – интерполяция управляющих точек кривых Безье.
  3. Использование групп – если анимируется несколько объектов одновременно.
  4. Применение easing и синусовидных функций – для более органичной анимации.

Пример морфинга сложного объекта:

const star = two.makeStar(200, 200, 50, 100, 5);
const flower = two.makePolygon(200, 200, 80, 6);

two.bind('update', function(frameCount) {
  const t = (Math.sin(frameCount * 0.03) + 1) / 2;
  for (let i = 0; i < star.vertices.length; i++) {
    star.vertices[i].x = star.vertices[i].x * (1 - t) + flower.vertices[i].x * t;
    star.vertices[i].y = star.vertices[i].y * (1 - t) + flower.vertices[i].y * t;
  }
}).play();

Важные рекомендации

  • Для плавной интерполяции не использовать paths с разным количеством вершин без нормализации.
  • Не смешивать кривые и прямые линии, если планируется морфинг.
  • Для динамических изменений форм полезно использовать двойное хранение вершин: исходные и целевые, чтобы избежать накопления ошибок при повторных интерполяциях.
  • Оптимизация: при большом количестве вершин лучше вычислять t заранее и обновлять координаты только при изменении t.

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