#javascript #canvas
#javascript #холст
Вопрос:
У меня есть следующий JS-код (найденный здесь, в stackoverflow, и немного модифицированный), который изменяет размер изображения на стороне клиента с помощью canvas.
function FileListItem(a) {
// Necesary to proper-work of CatchFile function (especially image-resizing).
// Code by Jimmy Wärting (https://github.com/jimmywarting)
a = [].slice.call(Array.isArray(a) ? a : arguments)
for (var c, b = c = a.length, d = !0; b-- amp;amp; d;) d = a[b] instanceof File
if (!d) throw new TypeError('expected argument to FileList is File or array of File objects')
for (b = (new ClipboardEvent('')).clipboardData || new DataTransfer; c--;) b.items.add(a[c])
return b.files
}
function CatchFile(obj) {
// Based on ResizeImage function.
// Original code by Jimmy Wärting (https://github.com/jimmywarting)
var file = obj.files[0];
// Check that file is image (regex)
var imageReg = /[/.](gif|jpg|jpeg|tiff|png|bmp)$/i;
if (!file) return
var uploadButtonsDiv = document.getElementById('upload_buttons_area');
// Check, that it is first uploaded file, or not
// If first, draw a div for showing status
var uploadStatusDiv = document.getElementById('upload_status_area');
if (!uploadStatusDiv) {
var uploadStatusDiv = document.createElement('div');
uploadStatusDiv.setAttribute('class', 'upload-status-area');
uploadStatusDiv.setAttribute('id', 'upload_status_area');
uploadButtonsDiv.parentNode.insertBefore(uploadStatusDiv, uploadButtonsDiv.nextSibling);
// Draw sub-div for each input field
var i;
for (i = 0; i < 3; i ) {
var uploadStatus = document.createElement('div');
uploadStatus.setAttribute('class', 'upload-status');
uploadStatus.setAttribute('id', ('upload_status_id_commentfile_set-' i '-file'));
uploadStatusDiv.append(uploadStatus);
}
}
var canvasDiv = document.getElementById('canvas-area');
var currField = document.getElementById(obj.id);
var currFieldLabel = document.getElementById(('label_' obj.id));
// Main image-converting procedure
if (imageReg.test(file.name)) {
file.image().then(img => {
const canvas = document.createElement('canvas')
canvas.setAttribute('id', ('canvas_' obj.id));
const ctx = canvas.getContext('2d')
const maxWidth = 1600
const maxHeight = 1200
// Calculate new size
const ratio = Math.min(maxWidth / img.width, maxHeight / img.height)
const width = img.width * ratio .5|0
const height = img.height * ratio .5|0
// Resize the canvas to the new dimensions
canvas.width = width
canvas.height = height
// Drawing canvas-object is necessary to proper-work
// on mobile browsers.
// In this case, canvas is inserted to hidden div (display: none)
ctx.drawImage(img, 0, 0, width, height)
canvasDiv.appendChild(canvas)
// Get the binary (aka blob)
canvas.toBlob(blob => {
const resizedFile = new File([blob], file.name, file)
const fileList = new FileListItem(resizedFile)
// Temporary remove event listener since
// assigning a new filelist to the input
// will trigger a new change event...
obj.onchange = null
obj.files = fileList
obj.onchange = CatchFile
}, 'image/jpeg', 0.70)
}
)
// If file is image, during conversion show status
function ShowConvertConfirmation() {
if (document.getElementById('canvas_' obj.id)) {
document.getElementById(('upload_status_' obj.id)).innerHTML =
'<font color="#4CAF50">Konwertowanie pliku ' file.name ' zakończone!</font>';
return true;
}
else {
document.getElementById(('upload_status_' obj.id)).innerHTML =
'<font color="#4CAF50">Konwertowanie pliku ' file.name ' zakończone!</font>';
return false;
}
}
// Loop ShowConvertConfirmation function untill return true (file is converted)
var convertConfirmationLoop = setInterval(function() {
var isConfirmed = ShowConvertConfirmation();
if (!isConfirmed) {
ShowConvertConfirmation();
}
else {
// Break loop
clearInterval(convertConfirmationLoop);
}
}, 2000); // Check every 2000ms
}
// If file is not an image, show status with filename
else {
document.getElementById(('upload_status_' obj.id)).innerHTML =
'<font color="#4CAF50">Dodano plik ' file.name '</font>';
//uploadStatusDiv.append(uploadStatus);
}
}
Холст рисуется в скрытом div:
<div id="canvas-area" style="overflow: hidden; height: 0;"></div>
Я только определяю, что представлена canvas-area div, и, основываясь на этом, JS добавляет другой div со статусом.
К сожалению, на некоторых мобильных устройствах (смартфонах среднего класса) сообщение будет отображаться до завершения рисования (это неправильно). Из-за этого некоторые загруженные изображения повреждены или остаются в исходном размере.
Как это предотвратить?
Комментарии:
1. Все, что должно произойти после загрузки изображения, должно быть в
then
обратном вызове (или вызываться изнутри него).2. @trincot спасибо. После нескольких дней тестов я могу подтвердить, что работает хорошо!
3. Опубликовано в качестве ответа 😉
Ответ №1:
Все, что должно произойти после загрузки изображения, должно выполняться в then
обратном вызове или вызываться изнутри него.
Важно понимать, что код, который не входит в этот обратный вызов, будет выполнен немедленно, задолго до завершения рисования.