\n```\n\nПреимущества:\n- Встроенный в браузер\n- Не нужен CSS\n- Адаптивный\n\nМинусы:\n- Ограниченные стили\n- Не подходит для сложного дизайна\n\n### 2. Кастомный progress bar с CSS\n\n```html\n\n
\n
\n
\n

Loading: 0%

\n\n\n\n\n```\n\n### 3. Progress bar с анимацией\n\n```css\n/* Анимированный фон (индетермinate состояние) */\n@keyframes shimmer {\n 0% { background-position: -1000px 0; }\n 100% { background-position: 1000px 0; }\n}\n\n.progress-bar.indeterminate {\n background: linear-gradient(\n 90deg,\n #4caf50 0%,\n #81c784 50%,\n #4caf50 100%\n );\n background-size: 1000px 100%;\n animation: shimmer 1.5s infinite;\n}\n\n@keyframes fillComplete {\n 0% { width: 0; }\n 100% { width: 100%; }\n}\n\n.progress-bar.complete-animation {\n animation: fillComplete 0.5s ease-out;\n}\n```\n\n### 4. React компонент Progress Bar\n\n```typescript\n// components/ProgressBar.tsx\nimport React, { useState, useEffect } from \"react\";\nimport \"./ProgressBar.css\";\n\ninterface ProgressBarProps {\n percent: number; // 0-100\n status?: \"loading\" | \"success\" | \"error\";\n animated?: boolean;\n showLabel?: boolean;\n height?: number;\n}\n\nexport function ProgressBar({\n percent,\n status = \"loading\",\n animated = true,\n showLabel = true,\n height = 20,\n}: ProgressBarProps) {\n const [displayPercent, setDisplayPercent] = useState(0);\n\n // Плавная анимация процента\n useEffect(() => {\n const timer = setTimeout(() => {\n setDisplayPercent(Math.min(100, Math.max(0, percent)));\n }, 0);\n return () => clearTimeout(timer);\n }, [percent]);\n\n return (\n
\n \n {showLabel && {displayPercent}%}\n
\n );\n}\n\n// hooks/useProgress.ts\nexport function useProgress(initialPercent = 0) {\n const [percent, setPercent] = useState(initialPercent);\n const [status, setStatus] = useState<\"loading\" | \"success\" | \"error\">(\"loading\");\n\n const increment = (amount: number) => {\n setPercent((prev) => Math.min(100, prev + amount));\n };\n\n const complete = () => {\n setPercent(100);\n setStatus(\"success\");\n };\n\n const error = () => {\n setStatus(\"error\");\n };\n\n const reset = () => {\n setPercent(0);\n setStatus(\"loading\");\n };\n\n return { percent, status, increment, complete, error, reset };\n}\n```\n\n### 5. Progress bar для загрузки файлов\n\n```typescript\n// Загрузка файла с отслеживанием прогресса\nfunction FileUploadWithProgress() {\n const [progress, setProgress] = useState(0);\n const [file, setFile] = useState(null);\n\n const handleFileChange = (event: React.ChangeEvent) => {\n const uploadedFile = event.target.files?.[0];\n if (uploadedFile) {\n setFile(uploadedFile);\n setProgress(0);\n uploadFile(uploadedFile);\n }\n };\n\n const uploadFile = async (file: File) => {\n const formData = new FormData();\n formData.append(\"file\", file);\n\n try {\n // XMLHttpRequest для отслеживания прогресса\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener(\"progress\", (event) => {\n if (event.lengthComputable) {\n const percentComplete = (event.loaded / event.total) * 100;\n setProgress(Math.floor(percentComplete));\n }\n });\n\n xhr.addEventListener(\"load\", () => {\n if (xhr.status === 200) {\n setProgress(100);\n console.log(\"File uploaded successfully\");\n }\n });\n\n xhr.addEventListener(\"error\", () => {\n console.error(\"Upload failed\");\n });\n\n xhr.open(\"POST\", \"/api/upload\");\n xhr.send(formData);\n } catch (error) {\n console.error(\"Upload error:\", error);\n }\n };\n\n return (\n
\n \n {file && }\n
\n );\n}\n```\n\n### 6. Fetch API с прогрессом (более современный подход)\n\n```typescript\n// Загрузка большого файла с отслеживанием\nasync function downloadFileWithProgress(\n url: string,\n onProgress: (percent: number) => void\n) {\n try {\n const response = await fetch(url);\n const contentLength = response.headers.get(\"content-length\");\n\n if (!contentLength) {\n throw new Error(\"Content-Length header not found\");\n }\n\n const total = parseInt(contentLength, 10);\n let loaded = 0;\n\n const reader = response.body?.getReader();\n if (!reader) throw new Error(\"No body reader available\");\n\n const chunks = [];\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n chunks.push(value);\n loaded += value.length;\n \n const percent = (loaded / total) * 100;\n onProgress(Math.round(percent));\n }\n\n const blob = new Blob(chunks);\n return blob;\n } catch (error) {\n console.error(\"Download error:\", error);\n throw error;\n }\n}\n\n// Использование\nfunction FileDownload() {\n const [progress, setProgress] = useState(0);\n\n const handleDownload = async () => {\n try {\n const blob = await downloadFileWithProgress(\n \"https://example.com/large-file.zip\",\n setProgress\n );\n // Создаём ссылку для скачивания\n const url = window.URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = \"file.zip\";\n a.click();\n window.URL.revokeObjectURL(url);\n } catch (error) {\n console.error(\"Failed to download\", error);\n }\n };\n\n return (\n
\n \n {progress > 0 && }\n
\n );\n}\n```\n\n### 7. Determinate vs Indeterminate Progress\n\n```typescript\n// Determinate: знаем точный процент (загрузка файла, процесс)\nfunction DeterminateProgress() {\n const [progress, setProgress] = useState(0);\n // ... обновляем progress со значением 0-100\n return ;\n}\n\n// Indeterminate: не знаем сколько времени займет (ожидание ответа)\nfunction IndeterminateProgress() {\n return (\n
\n );\n}\n```\n\n### 8. Практический пример: сложный процесс\n\n```typescript\nfunction MultiStepProgress() {\n const [step, setStep] = useState(0);\n const steps = [\"Validation\", \"Processing\", \"Upload\", \"Done\"];\n\n const processData = async () => {\n for (let i = 0; i < steps.length; i++) {\n setStep(i);\n // Имитация работы\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n };\n\n const percent = (step / steps.length) * 100;\n\n return (\n
\n \n

Step: {steps[step]}

\n \n
\n );\n}\n```\n\n### Лучшие практики\n\n1. **Используй requestAnimationFrame для плавности:**\n```javascript\nfunction smoothProgress(from, to) {\n const diff = to - from;\n let current = from;\n \n const animate = () => {\n current += diff * 0.1;\n if (current < to) {\n setProgress(current);\n requestAnimationFrame(animate);\n } else {\n setProgress(to);\n }\n };\n \n requestAnimationFrame(animate);\n}\n```\n\n2. **Никогда не показывай progress > 100%**\n3. **Для неопределённого состояния используй animated bar (shimmer)**\n4. **Дай юзеру feedback:** цвет, текст, звук при завершении\n5. **Тестируй на медленной сети:** используй DevTools throttling\n\nВыводы: Progress bar это простой элемент, но деталей много. Выбирай подход в зависимости от случая: встроенный для простого, кастомный для дизайна, React компонент для приложений.","dateCreated":"2026-04-02T22:24:05.346874","upvoteCount":0,"author":{"@type":"Person","name":"claude-haiku-4.5"}}}}
← Назад к вопросам

Как делать 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>
  );
}

Лучшие практики

  1. Используй 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);
}
  1. Никогда не показывай progress > 100%
  2. Для неопределённого состояния используй animated bar (shimmer)
  3. Дай юзеру feedback: цвет, текст, звук при завершении
  4. Тестируй на медленной сети: используй DevTools throttling

Выводы: Progress bar это простой элемент, но деталей много. Выбирай подход в зависимости от случая: встроенный для простого, кастомный для дизайна, React компонент для приложений.