Applets poster component

Github.com/jasondu/wxa…

demand

A small program can only be shared to the circle of friends by using the poster of the small program code. There are two ways to generate the small program code, one is to use the back-end way, the other is to use the canvas of the small program; The way of back-end development is difficult, because the generation of pictures consumes large memory is not small pressure on the server; Therefore, it is a good choice to use canvas of small program. However, due to the deep water and many pits of canvas, and the need to reproduce the rendering process of different posters, the code redundancy is difficult to maintain. In addition, the situation of different device versions is different, so there is an urgent need for poster generation components of small program.

In actual development, I found that there were no more than a few elements in the poster, and once these were implemented, various posters could be generated from a single configuration file.

Classification of elements in the poster

Problems to be solved

  • Unit problem

  • Canvas hidden problem

  • Rounded rectangle, rounded picture

  • More paragraphs

  • Long text and multi-line text shortening problems

  • Rectangle contains text

  • Hierarchy issues between multiple elements

  • Inconsistency between image size and render size

  • Turn canvas pictures

  • IOS 6.6.7 Clip problem

  • About getting the Canvas instance

Unit problem

Canvas drawing uses PX units, but px units of different devices need to be converted, so RPX units are uniformly used in components, which involves how to convert units.

Wx. getSystemInfoSync to get the screen size of the device to get the scale, and then do the conversion. The code is as follows:

const sysInfo = wx.getSystemInfoSync();
const screenWidth = sysInfo.screenWidth;
this.factor = screenWidth / 750;	// Get the ratio
function toPx(rpx) {	/ / the RPX px
	return rpx * this.factor;
}
function toRpx(px) {	/ / turn the RPX px
	return px / this.factor;
},
Copy the code

Canvas hidden problem

During the poster drawing process, we don’t want the user to see the canvas, so we have to hide the canvas. The initial thought is to use display: None; However, this will be blank when it is converted into a picture, so this will not work, so we can only control the absolute positioning of the canvas and move it out of the visual interface, the code is as follows:

.canvas.pro {
    position: absolute;
    bottom: 0;
    left: -9999rpx;
}
Copy the code

Rounded rectangle, rounded picture

Canvas does not provide ready-made API for rounded corners, so we have to draw by hand. In fact, the rounded rectangle is composed of 4 lines (yellow) and 4 arcs (red), as follows:

Arcs can be implemented using the canvasContext.arcto API, which takes inputs consisting of two control points and a radius, as shown in the example above

canvasContext.arcTo(x1, y1, x2, y2, r)
Copy the code

Now we can write the function that generates the rounded rectangle very easily

/** * draw rounded rectangles */
_drawRadiusRect(x, y, w, h, r) {
    const br = r / 2;
    this.ctx.beginPath();
    this.ctx.moveTo(this.toPx(x + br), this.toPx(y));    		// Move to the point in the upper left corner
    this.ctx.lineTo(this.toPx(x + w - br), this.toPx(y));		// Draw the top line
    this.ctx.arcTo(this.toPx(x + w), this.toPx(y), this.toPx(x + w), this.toPx(y + br), this.toPx(br));													// Draw the arc in the upper right corner
    this.ctx.lineTo(this.toPx(x + w), this.toPx(y + h - br));	// Draw the line on the right
    this.ctx.arcTo(this.toPx(x + w), this.toPx(y + h), this.toPx(x + w - br), this.toPx(y + h), this.toPx(br));											  // Draw the lower right corner of the arc
    this.ctx.lineTo(this.toPx(x + br), this.toPx(y + h));		// Draw the bottom line
    this.ctx.arcTo(this.toPx(x), this.toPx(y + h), this.toPx(x), this.toPx(y + h - br), this.toPx(br));													// Draw the lower left arc
    this.ctx.lineTo(this.toPx(x), this.toPx(y + br));			// Draw the line on the left
    this.ctx.arcTo(this.toPx(x), this.toPx(y), this.toPx(x + br), this.toPx(y), this.toPx(br));													// Draw the top left arc
}
Copy the code

Use this.ctx.stroke() if you are drawing a line;

For drawing blocks, use this.ctx.fill();

Use it if you have rounded corners

this.ctx.clip();
this.ctx.drawImage(***);
Copy the code

The clip() method cuts arbitrary shapes and sizes from the original canvas. Once an area is clipped, all subsequent drawings are restricted to the clipped area (no access to other areas of the canvas). The current canvas area can be saved by using the save() method before using the clip() method and restored at any time later (via the restore() method).

More paragraphs

If you have multiple consecutive text segments of different formats, it would be impractical for the user to specify coordinates for each segment because the length of the previous segment is not fixed. The solution here is to use the CTx. measureText Api (supported by base library 1.9.90) to calculate the width of a segment. Remember that the unit of returned width is px (pit) to know the coordinates of the next text.

Long text and multi-line text shortening problems

Set the width of the text. MeasureText knows the width of the text by ctx.measureText. If the text exceeds the specified width, use “… “for the exceeded part. Instead of; For multi-line text, we found that the font height is approximately equal to the font size, and provided the lineHeight parameter so that the user can customize the lineHeight, so that we can know the y-coordinate of the next line.

Rectangle contains text

This also uses the CTx. measureText interface to control the width of the rectangle, but you can also set the paddingLeft and paddingRight fields.

You can set the text baseline to middle (this.ctx.settextbaseline (‘middle’);) , set the coordinate of the text to the center line of the rectangle. Horizontally centered this.ctx.settextalign (‘center’);

Hierarchy issues between multiple elements

Since canvas has no Api to set the level of elements to be drawn, it can only be drawn according to the way that the level after drawing is higher than the level before, so the user needs to pass in the zIndex field and use Array sort (array.prototype.sort) to draw according to the order.

Inconsistency between image size and render size

To draw an image we use the ctx.drawImage()API;

If you use drawImage(dx, dy, dWidth, dHeight), the image will be compressed to fit the drawing size, and the image will be deformed as shown below:

In base library 1.9.0, drawImage(sx, SY, sWidth, sHeight, dx, dy, dWidth, dHeight) is supported. Sx and SY are the coordinates of the upper left corner of the rectangle selection box of the source image. SWidth and sHeight are the width and height of the rectangular selection box of the source image, as shown below:

If the drawing size is wider than the source size, then the width of the drawing size is equal to the source width; On the contrary, the drawing size is higher than the source size, so the height of the drawing size is equal to the source height;

We can get the size of the source diagram through the wx.getimageInfo API;

Turn canvas pictures

After completion of the drawing canvas calls wx. CanvasToTempFilePathApi canvas to output images, so need to pay attention to, wx. CanvasToTempFilePath need to write in this. CTX. The draw callback, And using this interface in a component requires passing this (pit) in the second input parameter, as follows

this.ctx.draw(false, () => {
    wx.canvasToTempFilePath({
        canvasId: 'canvasid',
        success: (res) => {
            wx.hideLoading();
            this.triggerEvent('success', res.tempFilePath);
        },
        fail: (err) => {
            wx.hideLoading();
            this.triggerEvent('fail', err);
        }
    }, this);
});
Copy the code

IOS 6.6.7 Clip problem

In the IOS version 6.6.7 clip method in continuous cropping the image, only the first effective, this is the bug, WeChat official also confirmed (developers.weixin.qq.com/community/d…

About getting the Canvas instance

We can use wx.createcanvasContext to get the applet instance, but remember to use this as the second parameter in the component, as follows

this.ctx = wx.createCanvasContext('canvasid'.this);
Copy the code

How to use components

Github.com/jasondu/wxa…