onUpdate

onUpdate — один из ключевых колбэков в библиотеке mo.js, обеспечивающий доступ к состоянию анимации на каждом кадре её воспроизведения. Используется для синхронизации пользовательской логики с прогрессом анимации, позволяя динамически изменять свойства DOM-элементов, вычислять значения в реальном времени и интегрировать анимацию с внешними системами.

Колбэк вызывается при каждом обновлении кадра (frame tick), что делает его важным инструментом для создания сложных и реактивных визуальных эффектов.


Сигнатура и параметры

onUpdate(progress)

Параметры:

  • progress — число в диапазоне от 0 до 1, отражающее текущий прогресс анимации:

    • 0 — начало анимации
    • 1 — завершение

Значение интерполируется во времени с учётом easing-функции, заданной для конкретной анимации.


Базовое использование

const tween = new mojs.Tween({
  duration: 1000,
  onUpdate: (progress) => {
    console.log(progress);
  }
});

tween.play();

В этом примере при каждом кадре выводится текущее значение прогресса.


Связь с временной шкалой

onUpdate вызывается в рамках внутреннего цикла рендеринга mo.js, который основан на requestAnimationFrame. Это означает:

  • частота вызова зависит от FPS (обычно ~60 кадров в секунду)
  • при падении производительности количество вызовов уменьшается
  • значения progress остаются корректными независимо от FPS

Работа с DOM внутри onUpdate

Одно из основных применений — ручное управление стилями:

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

const tween = new mojs.Tween({
  duration: 1000,
  onUpdate: (p) => {
    const x = 200 * p;
    el.style.transform = `translateX(${x}px)`;
  }
});

Здесь реализовано линейное перемещение элемента по оси X.


Интерполяция значений

onUpdate часто используется вместе с пользовательской интерполяцией:

const lerp = (start, end, t) => start + (end - start) * t;

const tween = new mojs.Tween({
  duration: 1000,
  onUpdate: (p) => {
    const scale = lerp(1, 2, p);
    el.style.transform = `scale(${scale})`;
  }
});

Это даёт полный контроль над изменением параметров, независимо от встроенных механизмов mo.js.


Использование easing внутри onUpdate

Хотя mo.js автоматически применяет easing к анимациям, внутри onUpdate можно вручную учитывать или игнорировать его:

const easing = mojs.easing.ease.out;

const tween = new mojs.Tween({
  duration: 1000,
  onUpdate: (p) => {
    const eased = easing(p);
    el.style.opacity = eased;
  }
});

Таким образом достигается дополнительная гибкость в управлении поведением.


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

onUpdate позволяет синхронизировать несколько элементов:

const el1 = document.querySelector('.a');
const el2 = document.querySelector('.b');

const tween = new mojs.Tween({
  duration: 1000,
  onUpdate: (p) => {
    el1.style.transform = `translateX(${100 * p}px)`;
    el2.style.transform = `translateY(${100 * p}px)`;
  }
});

Один tween управляет сразу несколькими анимациями.


Доступ к внутреннему состоянию

Внутри onUpdate можно использовать замыкания для доступа к внешним переменным:

let counter = 0;

const tween = new mojs.Tween({
  duration: 1000,
  onUpdate: (p) => {
    if (p > 0.5 && counter === 0) {
      console.log('Половина анимации');
      counter++;
    }
  }
});

Это позволяет отслеживать события внутри анимации.


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

В составе Timeline:

const timeline = new mojs.Timeline();

const tween = new mojs.Tween({
  duration: 1000,
  onUpdate: (p) => {
    console.log('Timeline progress:', p);
  }
});

timeline.add(tween);
timeline.play();

Каждый tween внутри таймлайна сохраняет собственный onUpdate.


Производительность и оптимизация

Частый вызов onUpdate требует аккуратного подхода:

Рекомендации:

  • минимизировать вычисления внутри колбэка
  • избегать частых обращений к DOM (кэшировать элементы)
  • использовать transform и opacity вместо свойств, вызывающих reflow
  • не создавать новые объекты внутри функции

Плохо:

onUpdate: (p) => {
  el.style.left = (100 * p) + 'px'; // вызывает layout
}

Лучше:

onUpdate: (p) => {
  el.style.transform = `translateX(${100 * p}px)`;
}

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

onUpdate подходит для работы с canvas:

const ctx = canvas.getContext('2d');

const tween = new mojs.Tween({
  duration: 1000,
  onUpdate: (p) => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillRect(0, 0, 100 * p, 50);
  }
});

Позволяет управлять рендерингом без ограничений DOM.


Комбинирование с физикой и пользовательской логикой

Можно реализовать собственную физику:

const tween = new mojs.Tween({
  duration: 1000,
  onUpdate: (p) => {
    const gravity = 9.8;
    const t = p * 1; // время
    const y = 0.5 * gravity * t * t;
    el.style.transform = `translateY(${y}px)`;
  }
});

Такой подход позволяет выходить за рамки стандартных easing-функций.


Управление состоянием интерфейса

onUpdate может использоваться для:

  • обновления прогресс-баров
  • синхронизации текста
  • изменения классов
onUpdate: (p) => {
  progressBar.style.width = `${p * 100}%`;
}

Отличие от onStart и onComplete

Колбэк Когда вызывается
onStart один раз при запуске
onUpdate на каждом кадре
onComplete один раз при завершении

onUpdate — единственный, который обеспечивает непрерывный контроль.


Частые ошибки

1. Перегрузка логикой

onUpdate: (p) => {
  heavyCalculation(); // плохо
}

2. Создание функций внутри

onUpdate: (p) => {
  const f = () => {}; // лишнее
}

3. Частое обращение к DOM

onUpdate: (p) => {
  document.querySelector('.box') // дорого
}

Паттерны использования

Реактивная анимация:

onUpdate: (p) => {
  state.value = p;
}

Связка с внешними библиотеками:

onUpdate: (p) => {
  threeObject.rotation.y = p * Math.PI * 2;
}

Гибридные сценарии

onUpdate часто используется вместе с декларативной анимацией mo.js:

const shape = new mojs.Shape({
  x: { 0: 200 },
  duration: 1000,
  onUpdate: (p) => {
    console.log('Дополнительная логика', p);
  }
});

Встроенная анимация управляет свойствами, а onUpdate добавляет дополнительное поведение.


Контроль воспроизведения

onUpdate может влиять на сам tween:

onUpdate: function(p) {
  if (p > 0.8) {
    this.pause();
  }
}

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


Расширенные сценарии

  • синхронизация с аудио
  • управление скролл-анимациями
  • кастомные таймлайны
  • реализация собственных easing-движков

Итоговое понимание

onUpdate — это низкоуровневый механизм контроля анимации, предоставляющий полный доступ к её прогрессу. Он превращает mo.js из декларативного инструмента в гибкую платформу для построения сложных интерактивных систем, где каждая миллисекунда анимации может быть обработана и использована в пользовательской логике.