CANVAS graphics pit recording step

A recent demand for mobile end generated pictures and upload, stepped on a lot of pits, recorded here. Since this function is mainly focused on the use of canvas to draw images, and generate web / upload pictures, so this mostly pictures and related records.

Drawing section

onload

Initially when using canvas directly load the network picture, forget to consider the problem of image loading, and thus directly on the handwriting image.src = xxxfollowed by ctx.drawImagethe last picture did not find the network is drawn up, think of it before you can use pictures must first be loaded completely ctx.drawImagego drawing, otherwise the picture is not finished loading, canvasdirect draw a blank picture.

export class Canvas {
    // code here...
    // 加载图片
    addImage(src: string) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => {
                resolve(img);
            }
            img.src = src;
        });
    }
    // code here...
}
复制代码

Picture a cross-domain issues

When you're done, run the code originally thought this up, but found that due to the visit of the CDN picture address, Image by default does not support cross-domain access to resources must be manually specify crossOriginattributes can access cross-domain images.

export class Canvas {
    // code here...
    // 加载图片
    addImage(src: string) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.crossOrigin = 'anonymous';
            img.onload = () => {
                resolve(img);
            }
            img.src = src;
        });
    }
    // code here...
}
复制代码

Rendering hierarchy

After the image is loaded up, feeling had been resolved, but found that I was supposed to render three pictures, and finally came out of a background image, drawing the other two are gone, but the print log can be seen in the code inside canvas indeed carried out these two pictures rendering logic.

Search of the investigation found that information only in accordance with the hierarchical relationship canvas rendering the order to show the drawing can not be specified levels manually, so we want to draw the picture above background, it is necessary to wait until the background drawing is completed before proceeding to the other logic.

export class Canvas {
    canvas: HTMLCanvasElement;
    ctx: CanvasRenderingContext2D;
    constructor() {
        // other code
        this.canvas = document.createElement('canvas');
        this.ctx = this.canvas.getContext('2d')!;
        // other code
    }
    // code here...
    addImage(src: string) {
        // ...
    }
    drawImage() {
        const bg = 'https://xxx';
        this.addImage(bg).then((img: ImageBitmap) => {
            this.ctx.drawImage(img, 0, 0, img.width, img.height);
            // 在这之后才能继续绘制其他图片
            this.addImage(xxx);
        });
    }
}
复制代码

Draw a circle picture

After all drawing is over, the product suddenly to me and says, want to add in the final surface area of a line of user information, including user avatar and user name, originally I thought it was a very simple task, in accordance with the above logic and then draw a picture a text can be later found user avatar pictures are square, but the product hope to be a round avatar picture, in cssneed only a simple line border-radius: 50%of something that makes me a headache.

After trying a variety of approaches have failed, checked the Internet to find information I realized that contextthere are save and restore methods, coupled with the clipcutting you can complete a picture of the round!

export class Canvas {
    constructor() {
        // ...
    }
    addImage() {
        // ...
    }
    drawImage() {
        // ...
    }
    drawUserInfo() {
        // other code
        const src = 'https://xxx';
         this.addImage(src).then((img: ImageBitmap) => {
            this.ctx.save();
            this.ctx.arc(x, y, r, 0, Math.PI * 2);
            this.ctx.clip();
            this.ctx.drawImage(img, x, y, img.width, img.height);
            this.ctx.restore();
        });
    }
}
复制代码

So first save the current state of the canvas, and then arcdraw a circle in the position of the avatar picture, then cut off the excess part, then draw the picture, and finally restore the canvas state can be.

Fixes

3x map

After the above steps will be finished, I tested CDN draw pictures and upload function, everything is normal!

Then, when this picture show full of joy, and found to show up on the phone is too vague, and even the words are not clear.

Then I realized we usually use pictures are 2x or 3x map, and now I follow the UI draft of 360pxthis picture drawn only 1x width drawings to show up on our high-resolution mobile phone will be very vague, and therefore to make the picture is not blurred, and I need the picture becomes 3x map.

Thus, when drawing all of the width and height of the array and all other × 3.

this.width = 360 * 3;
this.height = 500 * 3;

this.ctx.drawImage(bg, 0, 0, this.width * 3, this.height * 3);
// 其他改动同理
复制代码

After this change, showing out of the picture is very clear!

Cached images lead to the main draw card

After due to the need to load multiple pictures, so here I need to listen and draw all the pictures are loaded successfully, you can perform the ultimate canvas.toBlob()logic and upload pictures.

export class Canvas {
    constructor() {
        // ...
        this.loadedImageNumber = 0;
    }
    // code here
    imageOnLoaded() {
        this.loadedImageNumber++;
        if(this.laodedImageNumber >= 3) {
            // upload image
        }
    }
}
复制代码

Pm when the results of the test and qa students, often found stuck upload process, has been in a loading state, and later after numerous attempts and troubleshoot problems, because some found in front of the picture has been loaded once, the picture is possible here from the browser which get's cache, and therefore will not perform the onloadfunction, thus leading to this.loadedImageNumberhave failed to reach 3 greater than or equal conditions, so it stuck in the loading state.

The solution is to look perfect addImagefunction, listening onloadsimultaneously to determine what img the state, if it is completethen also survive a callback logic, by the way also add a bit about the onerrorprocess.

export class Canvas {
    // code here...
    // 加载图片
    addImage(src: string) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => {
                resolve(img);
            }
            img.onerror = () => {
                // error callback
                reject();
            }
            img.src = src;
            if(img.complete) {
                resolve(img);
            }
        });
    }
    // code here...
}
复制代码

Such test it, all pictures can be properly loaded out ~

polyfill

After the above problem has been solved, QA feedback students have some low-end phones are still stuck in the loading state, I would have thought it was the picture drawn still have problems, then I borrowed cell phone to debugging a bit and found the card in the picture is not drawing process but canvas.toBlob()when given, so I stuck behind the logic.

export class Canvas {
    // code here
    imageOnLoaded() {
        this.loadedImageNumber++;
        if(this.laodedImageNumber >= 3) {
            this.canvas.toBlob(blob => {
                // 真正的上传函数
                this.uploadImage(blob);
            },
            'image/jpeg',
            1.0
            )
        }
    }
    // code here
}
复制代码

Once again I checked the Internet to find information, find the need to fight polyfill job.

github地址:JavaScript-Canvas-to-Blob

readme many online article describes the use of, or look directly github official website is also very easy to understand.

if(__BROWSER__) {
    // import canvas toblob polyfill
    require('blueimp-canvas-to-blob');
}
export class Canvas {
    // ...
}
复制代码

__BROWSER__It is webpack.DefinePlugindefined by the client rendering environment.

This time should be feeling everything will be fine.

Optimizing image size

Then again QA students looking for. .

QA feedback classmate said image upload too slow, weak net case for a long time before the end of loading, or even directly to the back-end interface to return a timeout has not yet ended upload pictures.

I looked at the picture upload packet capture interface, I found a bit slow, since the resulting picture too bulky, and more than a full 2.6M

Asked about his colleagues, it turned out to be the first picture fuzzy, I want to improve the picture quality, while the 3x change map again toBlob()when the picture quality is designated 1.0, resulting in a picture too large.

After the picture quality here is designated as a volume of about 0.8 about to come down, and the picture quality is actually not changed much.

export class Canvas {
    // code here
    imageOnLoaded() {
        this.loadedImageNumber++;
        if(this.laodedImageNumber >= 3) {
            this.canvas.toBlob(blob => {
                // 真正的上传函数
                this.uploadImage(blob);
            },
            'image/jpeg',
            0.8
            )
        }
    }
    // code here
}
复制代码

This feature is finally here to complete OwO

Reproduced in: https: //juejin.im/post/5cecb0b36fb9a07ee565feea

Guess you like

Origin blog.csdn.net/weixin_34295316/article/details/91425882