← Назад к вопросам
Как делать progress bar через JavaScript?
1.0 Junior🔥 141 комментариев
#JavaScript Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Progress Bar в JavaScript/React
Progress bar — это визуальный элемент, показывающий прогресс процесса (загрузка файла, выполнение операции, loading страницы). Рассмотрю несколько подходов от простого к сложному.
1. Простой progress bar (HTML5 + CSS + JS)
HTML5 встроенный элемент
<!-- Встроенный элемент progress -->
<progress id="myProgress" value="0" max="100"></progress>
<p>Progress: <span id="percentage">0</span>%</p>
<script>
const progressElement = document.getElementById("myProgress");
const percentageText = document.getElementById("percentage");
let progress = 0;
const interval = setInterval(() => {
if (progress < 100) {
progress += Math.random() * 30;
if (progress > 100) progress = 100;
progressElement.value = progress;
percentageText.textContent = Math.floor(progress);
} else {
clearInterval(interval);
}
}, 500);
</script>
Преимущества:
- Встроенный в браузер
- Не нужен CSS
- Адаптивный
Минусы:
- Ограниченные стили
- Не подходит для сложного дизайна
2. Кастомный progress bar с CSS
<!-- HTML структура -->
<div class="progress-container">
<div class="progress-bar" id="progressBar"></div>
</div>
<p>Loading: <span id="percentage">0</span>%</p>
<style>
.progress-container {
width: 100%;
height: 20px;
background-color: #e0e0e0;
border-radius: 10px;
overflow: hidden;
}
.progress-bar {
height: 100%;
width: 0%;
background: linear-gradient(90deg, #4caf50, #81c784);
transition: width 0.3s ease;
}
.progress-bar.success {
background: linear-gradient(90deg, #4caf50, #2e7d32);
}
.progress-bar.error {
background: linear-gradient(90deg, #f44336, #d32f2f);
}
</style>
<script>
function setProgress(percentage) {
const progressBar = document.getElementById("progressBar");
const percentageText = document.getElementById("percentage");
// Ограничиваем между 0 и 100
const clipped = Math.max(0, Math.min(100, percentage));
progressBar.style.width = clipped + "%";
percentageText.textContent = clipped;
}
// Пример: загрузка файла
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 15;
setProgress(progress);
if (progress >= 100) {
clearInterval(interval);
document.getElementById("progressBar").classList.add("success");
}
}, 300);
</script>
3. Progress bar с анимацией
/* Анимированный фон (индетермinate состояние) */
@keyframes shimmer {
0% { background-position: -1000px 0; }
100% { background-position: 1000px 0; }
}
.progress-bar.indeterminate {
background: linear-gradient(
90deg,
#4caf50 0%,
#81c784 50%,
#4caf50 100%
);
background-size: 1000px 100%;
animation: shimmer 1.5s infinite;
}
@keyframes fillComplete {
0% { width: 0; }
100% { width: 100%; }
}
.progress-bar.complete-animation {
animation: fillComplete 0.5s ease-out;
}
4. React компонент Progress Bar
// components/ProgressBar.tsx
import React, { useState, useEffect } from "react";
import "./ProgressBar.css";
interface ProgressBarProps {
percent: number; // 0-100
status?: "loading" | "success" | "error";
animated?: boolean;
showLabel?: boolean;
height?: number;
}
export function ProgressBar({
percent,
status = "loading",
animated = true,
showLabel = true,
height = 20,
}: ProgressBarProps) {
const [displayPercent, setDisplayPercent] = useState(0);
// Плавная анимация процента
useEffect(() => {
const timer = setTimeout(() => {
setDisplayPercent(Math.min(100, Math.max(0, percent)));
}, 0);
return () => clearTimeout(timer);
}, [percent]);
return (
<div className="progress-container" style={{ height: `${height}px` }}>
<div
className={`progress-bar ${status} ${animated ? "animated" : ""}`}
style={{
width: `${displayPercent}%`,
}}
/>
{showLabel && <span className="progress-label">{displayPercent}%</span>}
</div>
);
}
// hooks/useProgress.ts
export function useProgress(initialPercent = 0) {
const [percent, setPercent] = useState(initialPercent);
const [status, setStatus] = useState<"loading" | "success" | "error">("loading");
const increment = (amount: number) => {
setPercent((prev) => Math.min(100, prev + amount));
};
const complete = () => {
setPercent(100);
setStatus("success");
};
const error = () => {
setStatus("error");
};
const reset = () => {
setPercent(0);
setStatus("loading");
};
return { percent, status, increment, complete, error, reset };
}
5. Progress bar для загрузки файлов
// Загрузка файла с отслеживанием прогресса
function FileUploadWithProgress() {
const [progress, setProgress] = useState(0);
const [file, setFile] = useState<File | null>(null);
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const uploadedFile = event.target.files?.[0];
if (uploadedFile) {
setFile(uploadedFile);
setProgress(0);
uploadFile(uploadedFile);
}
};
const uploadFile = async (file: File) => {
const formData = new FormData();
formData.append("file", file);
try {
// XMLHttpRequest для отслеживания прогресса
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", (event) => {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
setProgress(Math.floor(percentComplete));
}
});
xhr.addEventListener("load", () => {
if (xhr.status === 200) {
setProgress(100);
console.log("File uploaded successfully");
}
});
xhr.addEventListener("error", () => {
console.error("Upload failed");
});
xhr.open("POST", "/api/upload");
xhr.send(formData);
} catch (error) {
console.error("Upload error:", error);
}
};
return (
<div>
<input type="file" onChange={handleFileChange} />
{file && <ProgressBar percent={progress} />}
</div>
);
}
6. Fetch API с прогрессом (более современный подход)
// Загрузка большого файла с отслеживанием
async function downloadFileWithProgress(
url: string,
onProgress: (percent: number) => void
) {
try {
const response = await fetch(url);
const contentLength = response.headers.get("content-length");
if (!contentLength) {
throw new Error("Content-Length header not found");
}
const total = parseInt(contentLength, 10);
let loaded = 0;
const reader = response.body?.getReader();
if (!reader) throw new Error("No body reader available");
const chunks = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
loaded += value.length;
const percent = (loaded / total) * 100;
onProgress(Math.round(percent));
}
const blob = new Blob(chunks);
return blob;
} catch (error) {
console.error("Download error:", error);
throw error;
}
}
// Использование
function FileDownload() {
const [progress, setProgress] = useState(0);
const handleDownload = async () => {
try {
const blob = await downloadFileWithProgress(
"https://example.com/large-file.zip",
setProgress
);
// Создаём ссылку для скачивания
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "file.zip";
a.click();
window.URL.revokeObjectURL(url);
} catch (error) {
console.error("Failed to download", error);
}
};
return (
<div>
<button onClick={handleDownload}>Download File</button>
{progress > 0 && <ProgressBar percent={progress} />}
</div>
);
}
7. Determinate vs Indeterminate Progress
// Determinate: знаем точный процент (загрузка файла, процесс)
function DeterminateProgress() {
const [progress, setProgress] = useState(0);
// ... обновляем progress со значением 0-100
return <ProgressBar percent={progress} />;
}
// Indeterminate: не знаем сколько времени займет (ожидание ответа)
function IndeterminateProgress() {
return (
<div className="progress-bar indeterminate"></div>
);
}
8. Практический пример: сложный процесс
function MultiStepProgress() {
const [step, setStep] = useState(0);
const steps = ["Validation", "Processing", "Upload", "Done"];
const processData = async () => {
for (let i = 0; i < steps.length; i++) {
setStep(i);
// Имитация работы
await new Promise((resolve) => setTimeout(resolve, 1000));
}
};
const percent = (step / steps.length) * 100;
return (
<div>
<ProgressBar percent={percent} />
<p>Step: {steps[step]}</p>
<button onClick={processData}>Start</button>
</div>
);
}
Лучшие практики
- Используй requestAnimationFrame для плавности:
function smoothProgress(from, to) {
const diff = to - from;
let current = from;
const animate = () => {
current += diff * 0.1;
if (current < to) {
setProgress(current);
requestAnimationFrame(animate);
} else {
setProgress(to);
}
};
requestAnimationFrame(animate);
}
- Никогда не показывай progress > 100%
- Для неопределённого состояния используй animated bar (shimmer)
- Дай юзеру feedback: цвет, текст, звук при завершении
- Тестируй на медленной сети: используй DevTools throttling
Выводы: Progress bar это простой элемент, но деталей много. Выбирай подход в зависимости от случая: встроенный для простого, кастомный для дизайна, React компонент для приложений.