Сжатие изображений с помощью JavaScript - PullRequest
0 голосов
/ 24 июня 2019

Используя 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...