Используя Javascript (на стороне клиента), мне нужно реализовать функцию сжатия изображений с размерами всегда фиксированными 640x640, аналогично тому, что делает Instagram. Мне нужно обрабатывать как портретные, так и ландшафтные изображения и при необходимости заполнять любые края белым цветом.
Я не уверен, с чего начать?
Самое близкое решение, которое я нашел, - пост ниже.
Как сжать изображение с помощью Javascript в браузере?
Однако, когда я устанавливаю ширину и высоту на фиксированное значение 640x640, некоторые изображения кажутся немного перетаскиваемыми по горизонтали или вертикали.
Вот код, который у меня есть. сжатие, кажется, работает, однако размеры 640x640 не устанавливаются.
// imageFile below is e.target.files[0] on the input file selected event
getCompressedImageBase64 = (imageFile) => {
const validFileExtensions = ['.jpg', '.jpeg', '.bmp', '.gif', '.png'];
const nameSplitArr = imageFile.name.toLowerCase().split('.');
const fileExtension = nameSplitArr[nameSplitArr.length - 1];
// check file type
if(validFileExtensions.indexOf(`.${fileExtension}`) === -1 || imageFile.size/1000000 > 10) {
return 'Incorrect file type or file size is too large, has to be less than 10MB';
} else {
return new Promise((resolve, reject) => {
// If file size > 500kB, resize such that width <= 1200, quality = 0.6
this.reduceFileSize(imageFile, 500*1024, 640, 640, 0.6, (compressedBlob) => {
let fileReader = new FileReader();
fileReader.onload = (e) => {
resolve(e.target.result);
};
fileReader.readAsDataURL(compressedBlob);
});
});
}
}
// Reference for compression
// https://stackoverflow.com/questions/14672746/how-to-compress-an-image-via-javascript-in-the-browser
reduceFileSize = (file, acceptFileSize, maxWidth, maxHeight, quality, callback) => {
if (file.size <= acceptFileSize) {
callback(file);
return;
}
let fileReader = new FileReader();
fileReader.addEventListener("load", () => {
let img = new Image();
img.onerror = () => {
URL.revokeObjectURL(this.src);
callback(file);
};
img.onload = () => {
URL.revokeObjectURL(this.src);
this.getExifOrientation(file, (orientation) => {
let w = img.width, h = img.height;
let scale = (orientation > 4 ?
Math.min(maxHeight / w, maxWidth / h, 1) :
Math.min(maxWidth / w, maxHeight / h, 1));
h = Math.round(h * scale);
w = Math.round(w * scale);
let canvas = this.imgToCanvasWithOrientation(img, w, h, orientation);
canvas.toBlob((blob) => {
callback(blob);
}, 'image/jpeg', quality);
});
};
img.src = URL.createObjectURL(file);
}, false);
fileReader.readAsDataURL(file);
}
getExifOrientation = (file, callback) => {
// Suggestion from http://code.flickr.net/2012/06/01/parsing-exif-client-side-using-javascript-2/:
if (file.slice) {
file = file.slice(0, 131072);
} else if (file.webkitSlice) {
file = file.webkitSlice(0, 131072);
}
let reader = new FileReader();
reader.onload = function(e) {
let view = new DataView(e.target.result);
if (view.getUint16(0, false) != 0xFFD8) {
callback(-2);
return;
}
let length = view.byteLength, offset = 2;
while (offset < length) {
let marker = view.getUint16(offset, false);
offset += 2;
if (marker == 0xFFE1) {
if (view.getUint32(offset += 2, false) != 0x45786966) {
callback(-1);
return;
}
let little = view.getUint16(offset += 6, false) == 0x4949;
offset += view.getUint32(offset + 4, little);
let tags = view.getUint16(offset, little);
offset += 2;
for (let i = 0; i < tags; i++)
if (view.getUint16(offset + (i * 12), little) == 0x0112) {
callback(view.getUint16(offset + (i * 12) + 8, little));
return;
}
}
else if ((marker & 0xFF00) != 0xFF00) break;
else offset += view.getUint16(offset, false);
}
callback(-1);
};
reader.readAsArrayBuffer(file);
}
// Derived from https://stackoverflow.com/a/40867559, cc by-sa
imgToCanvasWithOrientation = (img, rawWidth, rawHeight, orientation) => {
let canvas = document.createElement('canvas');
if (orientation > 4) {
canvas.width = rawHeight;
canvas.height = rawWidth;
} else {
canvas.width = rawWidth;
canvas.height = rawHeight;
}
if (orientation > 1) {
// console.log("EXIF orientation = " + orientation + ", rotating picture");
}
let ctx = canvas.getContext('2d');
switch (orientation) {
case 2: ctx.transform(-1, 0, 0, 1, rawWidth, 0); break;
case 3: ctx.transform(-1, 0, 0, -1, rawWidth, rawHeight); break;
case 4: ctx.transform(1, 0, 0, -1, 0, rawHeight); break;
case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
case 6: ctx.transform(0, 1, -1, 0, rawHeight, 0); break;
case 7: ctx.transform(0, -1, -1, 0, rawHeight, rawWidth); break;
case 8: ctx.transform(0, -1, 1, 0, 0, rawWidth); break;
}
ctx.drawImage(img, 0, 0, rawWidth, rawHeight);
return canvas;
}