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…