Анимация спиральных структур

Спиральные движения в анимации обычно строятся на основе параметрического задания координат во времени. Классическая форма задаётся через тригонометрические функции:

  • x(t) = r(t) · cos(θ(t))
  • y(t) = r(t) · sin(θ(t))

где радиус r(t) и угол θ(t) изменяются независимо. Именно разделение этих двух параметров позволяет формировать широкий класс спиралей: Архимедовы, логарифмические, затухающие и комбинированные.

В контексте mo.js такие траектории реализуются через пользовательские tween-значения, где каждый кадр пересчитывает координаты объекта.

const spiralPath = (t, centerX, centerY) => {
  const angle = t * Math.PI * 8;
  const radius = 4 + t * 120;

  return {
    x: centerX + Math.cos(angle) * radius,
    y: centerY + Math.sin(angle) * radius
  };
};

Здесь t нормализован в диапазоне от 0 до 1. Увеличение коэффициента при угле формирует плотность витков, а линейное увеличение радиуса создаёт раскрывающуюся спираль.


Интеграция пользовательской траектории в mo.js

Библиотека mo.js предоставляет механизм кастомных tween-значений через MojsTween и модификаторы onUpdate. Основной принцип заключается в том, что позиция объекта обновляется вручную на каждом шаге анимации.

const circle = new mojs.Shape({
  shape: 'circle',
  radius: 10,
  fill: '#4A90E2'
});

new mojs.Tween({
  duration: 3000,
  onUpdate: (progress) => {
    const pos = spiralPath(progress, 300, 300);

    circle
      .tune({
        x: pos.x,
        y: pos.y
      })
      .replay();
  }
}).play();

В подобных конструкциях важно учитывать, что tune() в mo.js не создаёт новую анимацию, а обновляет параметры существующего объекта. Это позволяет избегать лишних перерасчётов DOM и сохранять производительность при сложных траекториях.


Архимедова спираль как базовая модель движения

Архимедова спираль характеризуется линейным ростом радиуса:

  • r(t) = a + b · θ

При реализации в JavaScript это упрощается до линейной зависимости от прогресса анимации.

const archimedeanSpiral = (t, a, b, cx, cy) => {
  const angle = t * Math.PI * 10;
  const radius = a + b * angle;

  return {
    x: cx + Math.cos(angle) * radius,
    y: cy + Math.sin(angle) * radius
  };
};

При малых значениях b движение становится плотным и равномерным, при увеличении коэффициента витки начинают быстро расходиться. В mo.js такие траектории часто используются для частиц и burst-анимаций, создавая эффект расширяющегося вихря.


Логарифмическая спираль и эффект естественного роста

Логарифмическая спираль отличается экспоненциальным увеличением радиуса:

  • r(θ) = a · e^(bθ)

В анимации это создаёт визуально более «живое» расширение, характерное для природных форм.

const logarithmicSpiral = (t, a, b, cx, cy) => {
  const angle = t * Math.PI * 8;
  const radius = a * Math.exp(b * angle);

  return {
    x: cx + Math.cos(angle) * radius,
    y: cy + Math.sin(angle) * radius
  };
};

В mo.js подобная кривая часто применяется для создания эффектов распада или взрывного движения частиц, поскольку ускорение визуально воспринимается как естественное затухание структуры.


Использование mo.js Timeline для синхронизации спиральных систем

При работе с несколькими объектами, движущимися по спирали, требуется синхронизация фаз. mo.js предоставляет Timeline, позволяющий объединять несколько tween-объектов в единую систему времени.

const timeline = new mojs.Timeline();

const createSpiralParticle = (delay, color) => {
  const shape = new mojs.Shape({
    shape: 'circle',
    radius: 6,
    fill: color
  });

  const tween = new mojs.Tween({
    delay,
    duration: 2500,
    onUpdate: (p) => {
      const pos = archimedeanSpiral(p, 0, 20, 300, 300);

      shape.tune({
        x: pos.x,
        y: pos.y
      });
    }
  });

  timeline.add(tween);
};

createSpiralParticle(0, '#E74C3C');
createSpiralParticle(200, '#2ECC71');
createSpiralParticle(400, '#3498DB');

timeline.play();

Смещение delay создаёт эффект закручивания нескольких слоёв спирали, где каждый новый элемент повторяет траекторию с фазовым сдвигом.


Модификация траектории через easing-функции

Спиральные движения становятся значительно более выразительными при применении easing-функций не только ко времени, но и к радиусу. Это позволяет управлять плотностью витков.

const easeInOut = (t) =>
  t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;

Применение easing к радиусу:

const easedSpiral = (t, cx, cy) => {
  const e = easeInOut(t);
  const angle = e * Math.PI * 10;
  const radius = 20 + e * 150;

  return {
    x: cx + Math.cos(angle) * radius,
    y: cy + Math.sin(angle) * radius
  };
};

Такая модификация создаёт эффект «разгона», где частица начинает движение медленно, затем ускоряется и выходит на стабильную траекторию.


Спирали на основе SVG path и mo.js Shape Swirl

mo.js поддерживает работу с SVG-путями через интерполяцию координат. Это позволяет заменить аналитические функции на заранее подготовленные кривые.

SVG-спираль может быть описана как path:

const spiralPathSVG = "M300,300 C350,250 400,350 450,300 S500,250 550,300";

Использование через shape с path:

const swirl = new mojs.Shape({
  shape: 'path',
  path: spiralPathSVG,
  stroke: '#8E44AD',
  strokeWidth: 3,
  fill: 'none'
});

При необходимости анимации вдоль пути используется модификация progress:

new mojs.Tween({
  duration: 3000,
  onUpdate: (p) => {
    const point = swirl.el.getPointAtLength(p * swirl.el.getTotalLength());

    swirl.tune({
      x: point.x,
      y: point.y
    });
  }
}).play();

SVG-подход позволяет создавать сложные спиральные структуры, которые трудно выразить аналитически.


Многослойные спиральные поля частиц

Сложные визуальные эффекты достигаются через наложение нескольких спиралей с различными параметрами: скоростью вращения, радиусом и фазой.

const layers = 5;

for (let i = 0; i < layers; i++) {
  const offset = i * 0.6;

  new mojs.Tween({
    duration: 4000,
    repeat: 999,
    onUpdate: (p) => {
      const t = (p + offset) % 1;

      const pos = {
        x: 300 + Math.cos(t * Math.PI * 12) * (20 + t * 140),
        y: 300 + Math.sin(t * Math.PI * 12) * (20 + t * 140)
      };

      new mojs.Shape({
        shape: 'circle',
        radius: 4,
        fill: `rgba(52, 152, 219, ${1 - t})`,
        x: pos.x,
        y: pos.y
      }).play();
    }
  }).play();
}

В подобных системах ключевым моментом становится контроль количества создаваемых объектов. Без ограничения частоты генерации возникает перегрузка памяти и падение FPS.


Оптимизация вычислений спиральных анимаций

При построении плотных спиральных структур критически важно минимизировать математические операции внутри onUpdate. Тригонометрия и экспоненты становятся узким местом при большом количестве частиц.

Используются следующие подходы:

  • предвычисление таблиц sin/cos
  • кэширование значений радиуса
  • переиспользование объектов вместо создания новых
  • отказ от вложенных new внутри update-цикла

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

const sinTable = Array.from({ length: 1000 }, (_, i) =>
  Math.sin((i / 1000) * Math.PI * 2)
);

const cosTable = Array.from({ length: 1000 }, (_, i) =>
  Math.cos((i / 1000) * Math.PI * 2)
);

Использование:

const fastSpiral = (t) => {
  const idx = Math.floor(t * 999);
  const angle = t * Math.PI * 10;

  const radius = 30 + t * 120;

  return {
    x: 300 + cosTable[idx] * radius,
    y: 300 + sinTable[idx] * radius
  };
};

Геометрические вариации спиральных структур

Спираль не ограничивается двумерной плоскостью. В mo.js возможно создание псевдо-3D эффектов через масштабирование и изменение z-index через scale.

const pseudo3DSpiral = (t) => {
  const angle = t * Math.PI * 8;
  const radius = 20 + t * 160;
  const depth = t;

  return {
    x: 300 + Math.cos(angle) * radius,
    y: 300 + Math.sin(angle) * radius,
    scale: 0.3 + depth
  };
};

Изменение масштаба создаёт иллюзию приближения и удаления объекта, усиливая восприятие глубины спирали.


Комбинация спиральных систем с burst-анимациями mo.js

mo.js Burst позволяет генерировать радиальные выбросы частиц. При замене радиального распределения на спиральное получается эффект закрученного взрыва.

const burst = new mojs.Burst({
  radius: { 0: 200 },
  count: 20,
  children: {
    shape: 'circle',
    radius: 5,
    fill: '#F39C12'
  }
});

burst.play();

Модификация через пост-обработку координат:

burst.children.forEach((child, i) => {
  const angle = i * 0.3;
  const radius = i * 8;

  child.tune({
    x: Math.cos(angle) * radius,
    y: Math.sin(angle) * radius
  });
});

Это превращает радиальный burst в закрученную структуру, визуально напоминающую вихрь энергии.


Поведение спиралей под влиянием шумовых функций

Добавление перлин-подобного шума позволяет разрушить идеальную геометрию спирали и приблизить её к природным формам.

const noise = (t) => Math.sin(t * 12.9898) * 43758.5453 % 1;

const noisySpiral = (t) => {
  const angle = t * Math.PI * 10;
  const radius = 20 + t * 140;

  const n = noise(t);

  return {
    x: 300 + Math.cos(angle + n) * radius,
    y: 300 + Math.sin(angle + n) * radius
  };
};

Такое искажение особенно эффективно в визуализациях частиц, где идеальная симметрия воспринимается как искусственная.