-
Notifications
You must be signed in to change notification settings - Fork 630
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Улучшает рецепт «Загрузка файла с прогресс-баром» (#5495)
* Исправляет пример * Добавляет демо эмуляции загрузки * Исправляет разметку, блокирует кнопки на время загрузки * Уменьшает паузу * Добавляет пустые строки * Добавляет проверку статуса ответа * Улучшает реалистичность эмуляции * Уточняет полученную часть * Возвращает нативный инпут * Изменяет код основного файла в соответствии с демо. Часть 1 * Изменяет код основного файла в соответствии с демо. Часть 2 * Изменяет код основного файла в соответствии с демо. Часть 3 * Устраняет неточности и опечатки * Добавляет про отображение input * Обновляет видео * Пробелы-запятые * Переснимает видео покрупнее * Добавляет пояснение к демо и меняет поясняющий текст в скобках --------- Co-authored-by: Svetlana Korobtseva <[email protected]>
- Loading branch information
1 parent
fd7d239
commit 02d5bf8
Showing
5 changed files
with
535 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
Oops, something went wrong.