Skip to content

Commit

Permalink
Улучшает рецепт «Загрузка файла с прогресс-баром» (#5495)
Browse files Browse the repository at this point in the history
* Исправляет пример

* Добавляет демо эмуляции загрузки

* Исправляет разметку, блокирует кнопки на время загрузки

* Уменьшает паузу

* Добавляет пустые строки

* Добавляет проверку статуса ответа

* Улучшает реалистичность эмуляции

* Уточняет полученную часть

* Возвращает нативный инпут

* Изменяет код основного файла в соответствии с демо. Часть 1

* Изменяет код основного файла в соответствии с демо. Часть 2

* Изменяет код основного файла в соответствии с демо. Часть 3

* Устраняет неточности и опечатки

* Добавляет про отображение input

* Обновляет видео

* Пробелы-запятые

* Переснимает видео покрупнее

* Добавляет пояснение к демо и меняет поясняющий текст в скобках

---------

Co-authored-by: Svetlana Korobtseva <[email protected]>
  • Loading branch information
vitya-ne and skorobaeus authored Sep 17, 2024
1 parent fd7d239 commit 02d5bf8
Show file tree
Hide file tree
Showing 5 changed files with 535 additions and 126 deletions.
291 changes: 291 additions & 0 deletions recipes/progress/demos/emulation/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<title>Прогресс-бар и эмуляция загрузки файла — Рецепты — Дока</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap">
<style>
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}

body {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 50px;
color: #FFFFFF;
font-family: "Roboto", sans-serif;
font-size: 18px;
background-color: #18191c;
}

.form-upload {
display: flex;
flex-direction: column;
align-items: flex-end;
}

.form-upload__label {
display: flex;
align-items: center;
}

.form-upload__title {
max-width: 200px;
margin-right: 55px;
font-size: 24px;
font-weight: 500;
line-height: 1;
}

.form-upload__input {
text-transform: lowercase;
font-size: 18px;
font-weight: 300;
font-family: inherit;
}

.form-upload__input::file-selector-button {
min-width: 190px;
margin-right: 30px;
padding: 9px 15px;
border: none;
border-radius: 6px;
font-weight: inherit;
font-family: inherit;
cursor: pointer;
}

.form-upload__input, .form-upload__submit, progress, .form-upload__container {
width: 360px;
}

.form-upload__submit {
display: block;
margin-top: 25px;
padding: 9px 15px;
border: 2px solid transparent;
border-radius: 6px;
color: #000000;
font-size: 18px;
font-weight: 300;
font-family: inherit;
transition: background-color 0.2s linear;
}

.form-upload__submit:hover {
background-color: #FFFFFF;
cursor: pointer;
transition: background-color 0.2s linear;
}

.form-upload__submit:focus-visible,
.form-upload__submit:focus {
border: 2px solid #ffffff;
outline: none;
}

.form-upload__submit_purple {
background-color: #C56FFF;
}

progress {
height: 5px;
margin-top: 25px;
border: none;
background-color: #286C2D;
}

progress::-webkit-progress-bar {
border: none;
background-color: #286C2D;
}

progress::-webkit-progress-value {
background-color: #41E847;
}

progress::-moz-progress-bar {
border: none;
background-color: #41E847;
}

.form-upload__container {
margin-top: 10px;
font-size: 16px;
}

.form-upload__status:empty::before {
content: "Не загружено";
}

@media (max-width: 768px) {
body {
padding: 30px;
}

.form-upload {
width: min( 100vw - 30px, 360px );
max-width: 360px;
}

.form-upload__label {
display: block;
}

.form-upload__title {
display: block;
max-width: initial;
margin-right: 0;
margin-bottom: 25px;
}

.form-upload__label, .form-upload__submit, progress, .form-upload__container {
width: 100%;
}

.form-upload__input::file-selector-button {
min-width: initial;
margin-right: 10px;
}
}
</style>
<body>
<div class="demo-wrapper">
<form id="uploadForm" method="post" enctype="multipart/form-data" class="form-upload">
<label for="uploadForm_File" class="form-upload__label">
<span class="form-upload__title">Изображение:</span>
<input type="file" name="file_name" id="uploadForm_File" accept="image/*" class="form-upload__input">
</label>

<input type="submit" id="uploadForm_Submit" class="form-upload__submit form-upload__submit_purple" value="Загрузить файл">
<progress id="progressBar" value="0" max="100"></progress>
<div class="form-upload__container"><span id="uploadForm_Status" class="form-upload__status"></span><span id="uploadForm_Size"></span></div>
</form>
</div>

<script>
const BYTES_IN_MB = 1048576

const form = document.getElementById('uploadForm')
const submitButton = form.querySelector('.form-upload__submit')
const fileInput = form.querySelector('.form-upload__input')
const sizeText = form.querySelector('#uploadForm_Size')
const statusText = form.querySelector('.form-upload__status')
const progressBar = form.querySelector('#progressBar')

function resetProgress(status = '') {
statusText.textContent = status
sizeText.textContent = ''
progressBar.value = 0
}

function upload(fileToUpload) {
const formSent = new FormData()
formSent.append('uploadForm_File', fileToUpload)

const xhr = new XMLHttpRequest()
xhr.upload.addEventListener('progress', progressHandler, false)
xhr.addEventListener('load', loadHandler, false)
xhr.addEventListener('error', errorHandler);
xhr.open('POST', 'upload_processing.php')

xhr.send(formSent)
}

function updateProgress(loaded, total) {
const loadedMb = (loaded/BYTES_IN_MB).toFixed(1)
const totalSizeMb = (total/BYTES_IN_MB).toFixed(1)
const percentLoaded = Math.round((loaded / total) * 100)

progressBar.value = percentLoaded
sizeText.textContent = `${loadedMb} из ${totalSizeMb} МБ`
statusText.textContent = `Загружено ${percentLoaded}% | `
}

fileInput.addEventListener('change', function () {
const file = this.files[0]
if (file.size > 5 * BYTES_IN_MB) {
alert('Принимается файл до 5 МБ')
this.value = null
}

resetProgress()
})

form.addEventListener('submit', function (event) {
event.preventDefault()

if (fileInput.files.length > 0) {
const fileToUpload = fileInput.files[0]
fileInput.disabled = true
submitButton.disabled = true
resetProgress()
upload(fileToUpload)
} else {
alert('Сначала выберите файл')
}

return false
})

function progressHandler(event) {
updateProgress(event.loaded, event.total)
}

function loadHandler(event) {
if (event.target.status !== 200) {
errorHandler()
} else {
statusText.textContent = event.target.responseText
progressBar.value = 0
fileInput.disabled = false
submitButton.disabled = false
}
}

function errorHandler() {
const showError = Math.random() > .75
if (showError) {
resetProgress('Ошибка загрузки')
fileInput.disabled = false
submitButton.disabled = false
} else {
const file = fileInput.files[0]
simulateLoading(file)
}
}

function simulateLoading(file) {
const total = file.size
const chunkCount = Math.floor(Math.random() * 5) + 5
const chunkSize = Math.floor(total / chunkCount)
let loaded = Math.max(chunkSize, progressBar.value)
let timeout = Math.floor(Math.random() * 200)

for (let i = 0; i < chunkCount; i ++) {
setTimeout(function() {
updateProgress(loaded, total)
loaded+= chunkSize

if ( chunkCount - i === 1 ) {
fileInput.disabled = false
submitButton.disabled = false
}
}, timeout)

timeout = Math.floor(Math.random() * 1000) + 500
}
}
</script>
</body>
</html>
Loading

0 comments on commit 02d5bf8

Please sign in to comment.