Functional experience

First look at demo: Image compression

Project source: Github

The effect is as follows:

Realize the function

  1. Can get compressed Base64 images
  2. Can get the compressed image node, and can replace the image node in the document
  3. It can obtain the compressed canvas for second drawing and replace the canvas in the document
  4. Can get the compressed BLOb file
  5. Ability to download compressed images

The specific implementation

The front end realizes the compression function, from the picture size and quality two aspects: reduce the picture and reduce the picture quality

So we design an imageCompress class and pass in an option that takes:

File: URL or file Width: the width of the output image Height: the height of the output image mineType: the format of the output image, the default is Image/PNG. Quality: the image quality of the output image. The value ranges from 0 to 1. The default value is 1Copy the code

Since image loading is an asynchronous process, we need to create and invoke instance methods in then with new ImageCompress(option).then(instance => {}) with the help of promise. Because a file can be either a URL or a File object, you need to evaluate each case separately in your build function and return a promise at the end

1. Create this class

class imageCompress {
    constructor(options) {
        this._img = null; // Original image
        this._compressedImg = null; // Compressed image
        this._canvas = null; // Create canvas
        this._blob = null; // Create a blob
        
        // Default Settings
        this.options = {
            mimeType: 'image/png'.quality: 1};// Merge default and user parameters
        extend(this.options, options, true);

        // The file throws an error for incoming
        if(! options.file) {throw new Error('Picture not passed in');
        }

        let that = this;
        if (typeof options.file === 'string') {
            let img = (this._img = new Image());
            // Resolve cross-domain error reporting
            img.setAttribute('crossOrigin'.'anonymous');
            img.src = options.file;

            return new Promise((resolve, reject) = > {
                img.onload = function() {
                    resolve(that);
                };
                img.onerror = function(. args) {
                    reject(new Error('Image load failed'));
                };
            });
        } else if (typeof options.file === 'object' && options.file.name) {
            // Determine whether the file suffix is a picture
            if(! isAssetTypeAnImage(options.file.name)) {throw new Error('This file is not a picture');
            }
            let image = (this._img = new Image());
            return new Promise((resolve, reject) = > {
                if (window.URL) {
                    image.src = window.URL.createObjectURL(options.file);
                } else {
                    const reader = new FileReader();
                    reader.onload = e= > {
                        image.src = e.target.result;
                    };

                    reader.onabort = (a)= > {
                        reject(
                            new Error(
                                'Aborted to load the image with FileReader.')); }; reader.onerror =(a)= > {
                        reject(
                            new Error(
                                'Failed to load the image with FileReader.')); }; reader.readAsDataURL(options.file); } image.onload =function() {
                    resolve(that);
                };
                image.onerror = function(. args) {
                    reject(new Error('Image load failed')); }; }); }}}Copy the code

2. Obtain canvas and replace it with page Canvas

We have loaded and assigned the image to this._img in the constructor function, and then we create a canvas and draw the image with the size set to get the target canvas. Replace a node, first find out its parent, replace with a new node oldNode. ParentNode. ReplaceChild (newNode, oldNode);

// Get canvas, which can be used for secondary drawing
getCanvas() {
    if (!this._canvas) this._imagetoCanvas();
    return this._canvas;
}
// Private method, image to canvas
_imagetoCanvas() {
    let image = this._img;
    var cvs = (this._canvas = document.createElement('canvas'));
    var ctx = cvs.getContext('2d');
    cvs.width = this.options.width || image.width;
    // The height defaults to equal scale compression
    cvs.height = this.options.width
        ? (this.options.width * image.height) / image.width
        : image.height;
    ctx.drawImage(image, 0.0, cvs.width, cvs.height);
}
// Replace the document canvas node
replaceCanvasNode(oldNode) {
    let newNode = this.getCanvas();
    // Make the new node have the id, class name, and style of the old node
    newNode.style.cssText = oldNode.style.cssText;
    newNode.id = oldNode.id;
    newNode.className = oldNode.className;
    // Replace the old node with the new node
    oldNode.parentNode.replaceChild(this.getCanvas(), oldNode);
}
Copy the code

3. Obtain the compressed image base64

In the previous step, we have been able to get canvas. Call Canvas. ToDataURL (this.options.mimeType, this.options.quality) to get base64

getImageBase64() {
    let canvas = this.getCanvas()
    return canvas.toDataURL(this.options.mimeType, this.options.quality);
}
Copy the code

4. Obtain the compressed file

Access blob called canvas. ToBlob (callback, mimeType, quality), because this process is asynchronous, so back to promise

Resolve (blob) return promise.resolve(blob)
getCompressFile() {
    if (!this._canvas) this._imagetoCanvas();
    let that = this;
    return new Promise((resolve, reject) = > {
        that._canvas.toBlob(
            blob= > {
                that._blob = blob;
                resolve(blob);
            },
            that.options.mimeType, // Image type
            that.options.quality // Image quality
        );
    });
}
Copy the code

5. Obtain the compressed IMG node and replace it with the page IMG

Make this._compressedimg point to the compressed image. Our goal is to find the SRC attribute of the image, There are two methods url.createObjecturl (blob) and new FileReader().readasDataURL (blob), so we need to call the method getCompressFile implemented in Step 4 to get the bloB

// Get the compressed image node
getCompressImageNode() {
    // If the compressed image is already created, you do not need to create it again
    if (this._compressedImg && this._compressedImg.src)
        return Promise.resolve(this._compressedImg);
    let image = (this._compressedImg = new Image());
    return this.getCompressFile().then(blob= > {
        if (window.URL) {
            image.src = window.URL.createObjectURL(blob);
        } else {
            const reader = new FileReader();

            reader.onload = e= > {
                image.src = e.target.result;
            };
            // Terminate the event
            reader.onabort = (a)= > {
                return Promise.reject(
                    new Error('Aborted to load the image with FileReader.')); }; reader.onerror =(a)= > {
                return Promise.reject(
                    new Error('Failed to load the image with FileReader.')); }; reader.readAsDataURL(blob); }return Promise.resolve(image);
    });
}
// Replace the page image
replaceImageNode(oldNode) {
    this.getCompressImageNode().then(image= > {
        image.style.cssText = oldNode.style.cssText;
        image.id = oldNode.id;
        image.className = oldNode.className;
        oldNode.parentNode.replaceChild(image, oldNode);
    });
}
Copy the code

6. Download the compressed file

When this._compressedImg is assigned and its SRC attribute exists, an A tag can be created for download. If a compressed IMG is not created, call the getCompressImageNode() method created in the previous step to get the compressed IMG and download it

// Download the compressed file
downloadCompressFile(name = 'compress-file') {
    if (this.blob && this._compressedImg) {
        const dataURL = this._compressedImg.src;
        const link = document.createElement('a');
        link.download = name;
        link.href = dataURL;
        link.dispatchEvent(new MouseEvent('click'));
    } else {
        this.getCompressImageNode().then(image= > {
            const dataURL = image.src;
            const link = document.createElement('a');
            link.download = name;
            link.href = dataURL;
            link.dispatchEvent(new MouseEvent('click')); }); }}Copy the code

The demo test

To test the above 7 functions, the effect and code are as follows:

new imageCompress({
    file:
        'https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1477436123, & FM = 26 & gp = 0. 3577936229 JPG'.width: 500.quality: 1.mimeType: 'image/jpeg',
})
    .then(instance= > {
        // Get canvas, which can be used for self-processing
        let canvas = instance.getCanvas();
        let context = canvas.getContext('2d');
        context.moveTo(100.100);
        context.lineTo(50.50);
        context.stroke();

        // There is an image node in the replacement document
        instance.replaceImageNode(document.getElementById('img'));
        // Replace the canvas node in the document
        instance.replaceCanvasNode(document.getElementById('canvas'));
        // Get the compressed image node
        instance.getCompressImageNode().then(image= > {
           console.log(image)
        });

        // // Obtain a compressed BLOb file for uploading
        instance.getCompressFile().then(blob= >{});// Get the image base64
        let base64 = instance.getImageBase64();
        // Download the compressed file
        // instance.downloadCompressFile();
    })
    .catch(err= > {
        console.log(err);
    });
Copy the code

These are the 7 features I can think of for image compression. If you have any other requirements, please leave them in the comments section. If there are mistakes in the article, also welcome to point out!

More recommended

Advanced_front_end

Daily question

Webpack4 Build Vue application (createVue)

Canvas Advanced (1) Generation of two-dimensional code and scan code recognition

Canvas advanced (2) Write a NPM plug-in to generate a TWO-DIMENSIONAL code with logo

Canvas advanced (3) TS + Canvas rewrite “color discrimination” small game

Canvas advanced (four) to achieve a “scratch-off” game

Canvas advanced (5) to achieve the image filter effect

VUI create log (a) – picture lazy load command implementation

VUI create log (two) – – anti – shake throttling component implementation

Front-end algorithm Topic analysis (1)

Front-end algorithm topic analysis (2)

Simple Routing implementation — (Hash routing)

Simple Routing implementation — (History routing)