progress-callback в Velocity.js вызывается на каждом шаге анимации и предназначен для отслеживания текущего состояния выполнения tween-процесса в реальном времени. Он работает как низкоуровневый механизм наблюдения за прогрессом, позволяя синхронизировать внешние эффекты, интерфейсные изменения и дополнительные вычисления с ходом анимации.
Функция прогресса передаётся в виде опции объекта параметров:
Velocity(element, {
opacity: 1,
width: "100%"
}, {
duration: 2000,
progress: function(elements, percentComplete, timeRemaining, timeStart, tweenValue) {
// логика отслеживания прогресса
}
});
Параметры callback-функции:
progress вызывается на каждом кадре анимационного цикла, синхронно с
обновлением значений свойств. Интервал зависит от
requestAnimationFrame или fallback-таймеров в старых
окружениях.
Каждый вызов отражает текущее состояние интерполяции между стартовым и конечным значением. Важно учитывать, что:
Наиболее часто используемое значение — percentComplete.
Оно нормализовано в диапазоне:
0.0 → начало анимации
1.0 → завершение анимации
Пример использования для синхронизации интерфейса:
Velocity(box, { width: "500px" }, {
duration: 3000,
progress: function(elements, percentComplete) {
const percent = Math.round(percentComplete * 100);
document.querySelector("#status").textContent = percent + "%";
}
});
timeRemaining позволяет оценить оставшееся время анимации и использовать его для предиктивных эффектов:
Velocity(circle, { translateX: "300px" }, {
duration: 4000,
progress: function(elements, percentComplete, timeRemaining) {
if (timeRemaining < 1000) {
elements[0].style.opacity = percentComplete;
}
}
});
Практически применяется для:
tweenValue отражает текущее интерполированное значение
анимируемого свойства. Оно наиболее полезно при анимации числовых
параметров или кастомных свойств.
Пример:
Velocity(box, { left: 600 }, {
duration: 2000,
progress: function(elements, percentComplete, timeRemaining, timeStart, tweenValue) {
console.log(tweenValue);
}
});
При анимации нескольких свойств значение может быть усреднённым или относиться к первому tween-каналу, поэтому используется реже, чем percentComplete.
progress часто используется для построения связей между анимацией и интерфейсными элементами.
Velocity(loader, { width: "100%" }, {
duration: 5000,
progress: function(elements, percentComplete) {
document.querySelector(".bar").style.width = (percentComplete * 100) + "%";
}
});
const counter = document.querySelector(".counter");
Velocity(counter, { dummy: 1 }, {
duration: 3000,
progress: function(elements, percentComplete) {
const value = Math.floor(percentComplete * 1000);
counter.textContent = value;
}
});
progress отличается от complete по характеру вызова:
Типичный сценарий комбинирования:
Velocity(box, { opacity: 0 }, {
duration: 1500,
progress: function(elements, percentComplete) {
elements[0].style.transform = `scale(${1 - percentComplete * 0.5})`;
},
complete: function(elements) {
elements[0].style.display = "none";
}
});
Хотя percentComplete формально линейно возрастает от 0 до 1, фактическое изменение визуальных значений зависит от easing-функции.
Это приводит к тому, что:
progress может вызываться десятки раз в секунду, поэтому внутри callback недопустимы тяжёлые операции:
Оптимальный подход:
let lastUpdate = 0;
Velocity(box, { left: 300 }, {
duration: 3000,
progress: function(elements, percentComplete, timeRemaining, timeStart) {
const now = performance.now();
if (now - lastUpdate < 50) return;
lastUpdate = now;
requestAnimationFrame(() => {
elements[0].textContent = Math.round(percentComplete * 100);
});
}
});
Если анимация применяется к группе элементов, progress вызывается для всей группы, а не для каждого элемента отдельно.
Velocity(document.querySelectorAll(".item"), { opacity: 0 }, {
duration: 2000,
progress: function(elements, percentComplete) {
elements.forEach(el => {
el.style.transform = `translateY(${percentComplete * 20}px)`;
});
}
});
В таких случаях важно учитывать, что синхронизация будет общей для всех элементов.
progress может управлять переходом между этапами анимационной последовательности:
Velocity(box, { width: 500 }, {
duration: 2000,
progress: function(elements, percentComplete) {
if (percentComplete > 0.5) {
Velocity(elements[0], { backgroundColor: "#ff0000" }, { duration: 300 });
}
}
});
Подобные конструкции требуют осторожности, так как могут приводить к множественным триггерам анимаций.