В библиотеке mo.js процесс анимации построен вокруг непрерывного цикла обновлений, где каждый кадр пересчитывается текущее состояние tween или timeline. Центральным моментом этого цикла является стадия обновления, на которой вызываются callback-функции, позволяющие вмешиваться в ход анимации, отслеживать прогресс и синхронизировать внешние эффекты.
Обновление происходит синхронно с requestAnimationFrame, что обеспечивает согласованность с частотой рендеринга браузера. Каждый тик анимации приводит к вычислению нормализованного прогресса и последующему вызову обработчиков.
В mo.js существует несколько способов реагирования на обновление анимации:
onUpdate в конфигурации tween или
shape.on('update', ...)Обновление вызывается многократно в процессе жизни анимации, начиная с первого кадра и заканчивая завершением tween.
Каждый вызов содержит контекст текущего состояния анимации, включая прогресс и вычисленные значения свойств.
Функция обновления получает набор параметров, которые позволяют точно определить состояние анимации в текущий момент времени.
Наиболее важный параметр — нормализованный прогресс
progress, принимающий значения от 0 до 1.
const tween = new mojs.Tween({
duration: 2000,
onUpdate(progress) {
console.log(progress);
}
});
В зависимости от типа объекта mo.js сигнатура может расширяться:
progress — нормализованное значение выполненияel — связанный DOM-элемент (в некоторых модулях)tween или instance — ссылка на текущий
объект анимацииПрогресс используется как базовый параметр для интерполяции любых значений вручную, если требуется дополнительная логика поверх встроенного tweening-механизма.
Tween является базовой единицей анимации, и именно в нём update-колбэк проявляет себя наиболее явно.
const tween = new mojs.Tween({
duration: 3000,
onUpdate(progress) {
const value = progress * 100;
document.querySelector('.bar').style.width = value + '%';
}
});
tween.play();
В этом примере update-событие используется для синхронизации ширины DOM-элемента с прогрессом анимации. Каждый кадр пересчитывается значение и применяется к стилю.
Подобный подход превращает mo.js в инструмент не только для графических эффектов, но и для построения реактивных интерфейсных переходов.
Помимо конфигурационного onUpdate, mo.js предоставляет
событийный интерфейс, позволяющий регистрировать несколько обработчиков
на одно событие.
const tween = new mojs.Tween({
duration: 2000
});
tween
.on('update', (progress) => {
console.log('progress:', progress);
})
.on('update', (progress) => {
console.log('secondary handler:', progress);
});
tween.play();
Событийная модель полезна при необходимости разделения логики обновления на независимые слои: визуальный, аналитический и синхронизационный.
В shape-модулях mo.js update-событие тесно связано с трансформацией SVG-объектов. При каждом кадре пересчитываются параметры формы: радиус, поворот, масштаб, stroke-dashoffset и другие свойства.
const circle = new mojs.Shape({
shape: 'circle',
radius: { 0: 50 },
stroke: 'cyan',
strokeWidth: { 10: 0 },
duration: 1500,
onUpdate(progress) {
const opacity = 1 - progress;
this.el.style.opacity = opacity;
}
});
Здесь callback используется для дополнительного контроля над стилями, которые не всегда описываются декларативной конфигурацией анимации.
Внутри onUpdate контекст this часто
ссылается на экземпляр анимации. Это позволяет получать доступ к
внутренним свойствам tween или shape без внешних замыканий.
const tween = new mojs.Tween({
duration: 1000,
onUpdate(progress) {
this.progress = progress;
}
});
Такой подход используется для синхронизации состояния анимации с внешними системами, например, менеджерами состояния интерфейса.
Важно учитывать, что при использовании стрелочных функций контекст теряется:
onUpdate: (progress) => {
// this не является экземпляром mo.js
}
Callback обновления вызывается на каждом кадре анимации, что делает
его потенциально чувствительным к нагрузке. В сложных интерфейсах
чрезмерные вычисления внутри onUpdate приводят к падению
частоты кадров.
Основные ограничения:
Оптимизированный подход:
const el = document.querySelector('.box');
const tween = new mojs.Tween({
duration: 2000,
onUpdate(progress) {
el.style.transform = `translateX(${progress * 200}px)`;
}
});
Здесь DOM-элемент захватывается один раз, что снижает накладные расходы.
Обновление часто используется как механизм координации нескольких tween-ов или shape-объектов. Один update-колбэк может управлять сразу несколькими визуальными элементами.
const circle = new mojs.Shape({
shape: 'circle',
radius: { 0: 40 },
duration: 2000
});
const square = new mojs.Shape({
shape: 'rect',
radius: { 0: 30 },
duration: 2000
});
circle.on('update', (p) => {
square.setProgress(p);
});
Такой подход позволяет строить сложные анимационные сцены с единым источником времени.
Значение progress не всегда интерпретируется линейно.
mo.js поддерживает easing-функции, которые влияют на распределение
update-вызовов по времени.
При нелинейном easing:
const tween = new mojs.Tween({
duration: 2000,
easing: 'ease.out',
onUpdate(progress) {
const eased = Math.pow(progress, 2);
document.body.style.opacity = eased;
}
});
В timeline-структурах update вызывается на каждом шаге общего прогресса, агрегируя состояние вложенных tween-ов. Это позволяет получать глобальный контроль над всей сценой.
const timeline = new mojs.Timeline();
timeline.add(circle, square);
timeline.on('update', (progress) => {
console.log('timeline progress:', progress);
});
Timeline становится центральной точкой синхронизации, а update — механизмом распространения состояния вниз по дереву анимаций.
В реальных сценариях возникают повторяющиеся проблемы:
Update-цикл часто используется как основа реактивных систем, где состояние интерфейса зависит от прогресса анимации.
Типовой паттерн:
const state = {
value: 0
};
const tween = new mojs.Tween({
duration: 2000,
onUpdate(p) {
state.value = p;
render();
}
});
function render() {
document.querySelector('.label').textContent = state.value.toFixed(2);
}
Такой подход формирует предсказуемую модель обновления интерфейса, где анимация становится драйвером состояния.
В сложных сценах update используется как центральный механизм управления множеством зависимых элементов. Каждый кадр становится точкой синхронизации геометрии, прозрачности и трансформаций.
При построении композиции анимаций update часто превращается в слой оркестрации:
Такой подход позволяет строить сцены, где изменение одного tween влияет на всю систему анимации без жесткой связанности компонентов.