Индикаторы прогресса используются для визуализации состояния выполнения задачи: загрузки данных, обработки, перехода между этапами. В интерфейсах они снижают неопределённость и улучшают восприятие скорости работы приложения. В контексте Motion One такие элементы создаются через анимации, управляемые состоянием и временными функциями.
Существует два основных типа:
Motion One предоставляет декларативный и императивный подходы для анимации этих компонентов.
Минимальная структура:
<div class="progress">
<div class="progress-bar"></div>
</div>
.progress {
width: 100%;
height: 6px;
background: #eee;
overflow: hidden;
}
.progress-bar {
width: 0%;
height: 100%;
background: #3b82f6;
}
Анимация через Motion One:
import { animate } from "motion";
animate(".progress-bar", {
width: "75%"
}, {
duration: 1,
easing: "ease-out"
});
width) — основной способ отображения
прогрессаeasing делает движение естественнымПри работе с асинхронными операциями:
const progressBar = document.querySelector(".progress-bar");
function setProgress(value) {
animate(progressBar, {
width: value + "%"
}, {
duration: 0.4,
easing: "ease-out"
});
}
Пример:
setProgress(20);
setTimeout(() => setProgress(50), 1000);
setTimeout(() => setProgress(100), 2000);
Использование transform: scaleX предпочтительнее с точки
зрения производительности:
.progress-bar {
transform-origin: left;
transform: scaleX(0);
}
animate(".progress-bar", {
transform: "scaleX(0.75)"
}, {
duration: 1
});
Когда точный прогресс неизвестен:
.progress-bar {
width: 30%;
position: relative;
}
import { animate } from "motion";
animate(".progress-bar", {
x: ["-100%", "300%"]
}, {
duration: 1.2,
repeat: Infinity,
easing: "linear"
});
repeat: Infinity)HTML + SVG:
<svg width="100" height="100">
<circle
class="bg"
cx="50"
cy="50"
r="40"
stroke="#eee"
stroke-width="8"
fill="none"
/>
<circle
class="progress"
cx="50"
cy="50"
r="40"
stroke="#3b82f6"
stroke-width="8"
fill="none"
stroke-dasharray="251"
stroke-dashoffset="251"
/>
</svg>
const circumference = 2 * Math.PI * 40;
animate(".progress", {
strokeDashoffset: circumference * (1 - 0.75)
}, {
duration: 1,
easing: "ease-out"
});
stroke-dasharray задаёт длину окружностиstroke-dashoffset управляет видимой частьюИндикатор прогресса прокрутки страницы:
import { scroll } from "motion";
scroll(animate(".progress-bar", {
transform: ["scaleX(0)", "scaleX(1)"]
}), {
target: document.documentElement
});
Сложные сценарии:
import { timeline } from "motion";
timeline([
[".progress-bar", { transform: "scaleX(0.3)" }, { duration: 0.5 }],
[".progress-bar", { transform: "scaleX(0.6)" }, { duration: 0.5 }],
[".progress-bar", { transform: "scaleX(1)" }, { duration: 0.5 }]
]);
Интеграция с состоянием приложения:
let progress = 0;
function updateProgress(newValue) {
progress = newValue;
animate(".progress-bar", {
transform: `scaleX(${progress / 100})`
});
}
Можно связать с:
Добавление прозрачности и цвета:
animate(".progress-bar", {
transform: "scaleX(1)",
opacity: [0.5, 1],
backgroundColor: ["#60a5fa", "#3b82f6"]
}, {
duration: 1
});
Скрытие индикатора:
animate(".progress-bar", {
opacity: 0
}, {
duration: 0.3,
delay: 0.2
});
Или:
animate(".progress", {
height: 0
}, {
duration: 0.3
});
Ключевые рекомендации:
transform вместо widthwill-change: transform для сложных
анимацийanimate(".progress-bar", {
transform: "scaleX(1)"
}, {
type: "spring",
stiffness: 200,
damping: 20
});
Создаёт эффект «дорастания» до финального значения.
.progress-bar {
background: linear-gradient(90deg, #3b82f6, #06b6d4);
}
Анимация остаётся прежней, но визуально становится сложнее.
<div class="progress">
<div class="segment"></div>
<div class="segment"></div>
<div class="segment"></div>
</div>
Анимация по очереди:
animate(".segment", {
opacity: [0, 1]
}, {
delay: stagger(0.2)
});
width вместо transform при
высокой нагрузкеИндикатор должен:
Пример структуры:
class ProgressBar {
constructor(selector) {
this.el = document.querySelector(selector);
}
set(value) {
animate(this.el, {
transform: `scaleX(${value / 100})`
});
}
}
async function loadData() {
const bar = new ProgressBar(".progress-bar");
bar.set(20);
await fetch("/data");
bar.set(100);
}
Дополнение индикатора текстом:
<span class="progress-text">0%</span>
function update(value) {
document.querySelector(".progress-text").textContent = value + "%";
}
Синхронное обновление усиливает восприятие точности.
Важно соблюдать баланс:
1s — воспринимается как задержка
Motion One позволяет гибко управлять этими параметрами через
duration, delay, easing.
Прогресс может зависеть от пользовательского ввода:
import { animate, hover } from "motion";
hover(".button", () => {
animate(".progress-bar", {
transform: "scaleX(1)"
});
});
Для сложных интерфейсов:
animate(".progress-bar", {
transform: "scaleX(0)"
}, {
duration: 0.5
});
Применяется при отмене действий или откате.
Например:
Каждый анимируется отдельно, но синхронизируется логикой приложения.
Индикаторы прогресса в Motion One представляют собой гибкий инструмент, сочетающий высокую производительность, декларативность и точный контроль над анимацией, что позволяет реализовывать как простые полосы загрузки, так и сложные визуальные системы обратной связи.