vue-advanced-cropper 图像裁剪尺寸比原始尺寸大

vue-advanced-cropper image croped sized is bigger than the original

我正在实现一个用户选择图像的系统,他必须在保存之前裁剪图像;我是 using vue-advance-cropper 插件;所有系统都已设置,但结果图像尺寸比原始尺寸大;

示例:i insert image at 307ko i got 448ko; i insert image at 40ko i got 206ko;

是否有任何我错过的选项可以使结果的大小小于原始大小,或者没有什么可以做的?

摘要

我想所描述的问题与使用的库没有共同之处。

裁剪图像尺寸大于原始图像尺寸的最常见原因是裁剪图像格式与原始图像格式之间的差异。

澄清一下。如果你上传一张jpeg图片,裁剪它并通过裁剪器canvas通过toBlob方法获取它,默认情况下你会得到一张png图片,所以它显然会是大多数情况下比原始图像大。

那是因为 toBlob 从 canvas 创建了一个全新的图像,它对原始图像的属性一无所知。因此,根据这种方法的性质,您无法创建原始图像的精确副本,包括其大小。

好消息是您可以更接近原始图像。

toBlob 方法具有以下签名:

toBlob(callback)
toBlob(callback, type)
toBlob(callback, type, quality)

因此,您可以设置相同类型和图像质量的合理值。

解决方案

vue-advanced-cropper documentation中有上传图片的例子。我将在下面展示它的完整版本:

import { Cropper } from 'vue-advanced-cropper'

// This function is used to detect the actual image type, 
function getMimeType(file, fallback = null) {
    const byteArray = (new Uint8Array(file)).subarray(0, 4);
    let header = '';
    for (let i = 0; i < byteArray.length; i++) {
       header += byteArray[i].toString(16);
    }
    switch (header) {
        case "89504e47":
            return "image/png";
        case "47494638":
            return "image/gif";
        case "ffd8ffe0":
        case "ffd8ffe1":
        case "ffd8ffe2":
        case "ffd8ffe3":
        case "ffd8ffe8":
            return "image/jpeg";
        default:
            return fallback;
    }
}

export default {
    components: {
        Cropper,
    },
    data() {
        return {
            image: {
                src: null,
                type: null
            }
        };
    },
    methods: {
        crop() {
            const { canvas } = this.$refs.cropper.getResult();
            canvas.toBlob((blob) => {
                // Do something with blob: upload to a server, download and etc.
            }, this.image.type);
        },
        reset() {
            this.image = {
                src: null,
                type: null
            }
        },
        loadImage(event) {
            // Reference to the DOM input element
            const { files } = event.target;
            // Ensure that you have a file before attempting to read it
            if (files && files[0]) {
                // 1. Revoke the object URL, to allow the garbage collector to destroy the uploaded before file
                if (this.image.src) {
                    URL.revokeObjectURL(this.image.src)
                }
                // 2. Create the blob link to the file to optimize performance:
                const blob = URL.createObjectURL(files[0]);
                
                // 3. The steps below are designated to determine a file mime type to use it during the 
                // getting of a cropped image from the canvas. You can replace it them by the following string, 
                // but the type will be derived from the extension and it can lead to an incorrect result:
                //
                // this.image = {
                //    src: blob;
                //    type: files[0].type
                // }
                
                // Create a new FileReader to read this image binary data
                const reader = new FileReader();
                // Define a callback function to run, when FileReader finishes its job
                reader.onload = (e) => {
                    // Note: arrow function used here, so that "this.image" refers to the image of Vue component
                    this.image = {
                        // Set the image source (it will look like blob:http://example.com/2c5270a5-18b5-406e-a4fb-07427f5e7b94)
                        src: blob,
                        // Determine the image type to preserve it during the extracting the image from canvas:
                        type: getMimeType(e.target.result, files[0].type),
                    };
                };
                // Start the reader job - read file as a data url (base64 format)
                reader.readAsArrayBuffer(files[0]);
            }
        },
    },
    destroyed() {
        // Revoke the object URL, to allow the garbage collector to destroy the uploaded before file
        if (this.image.src) {
            URL.revokeObjectURL(this.image.src)
        }
    }
};

此示例将帮助您使裁剪结果大小非常接近原始图像大小。您可以在 the sandbox 中使用上面代码的较轻版本在线测试它。