In daily uniApp and small program development, whenever I need to draw a poster at work, it gives me a headache. I need to put my previous code (downloadFile asynchronous package, CTX drawing method….) Copied, and then introduce one by one, or I can’t find it, and go to baidu, then it will be a long day so wasted, so in order to reduce the introduction of these tools I made a dedicated to create a poster of a tool library, so that it can reduce my workload (HHH can touch fish swimming again. The following is the introduction of the plug-in library, we can have a look, the current function has been all perfect

Brief description of poster drawing tool

  • Create and draw a postercanvasRectangle method, built-in picture drawing, rounded rectangle drawing, newline font drawing and other methods.
  • Close to a native development experience, fast to work with, with only business logic to worry about and nothing else.
  • Has good syntax structure, will not drawuni/wxRectangle falls into callback hell.
  • Support native applets, withuniappMultiterminal applications. When the environment is native applets, automatically switch to better performancetype2dDrawing mode.
  • Combine complex logic into simple methods that are extensible and usableuse|useCtxIntroduce extensions.
  • supporttypescriptTo supportvue3Template, specific use referenceuseDrawPoster.

API documentation: U-draw-Poster

Plug-in market: Dcloud/U-draw-Poster

NPM installs plug-ins

npm i --save-dev u-draw-poster
Copy the code

Enable UNI conditional compilation for this plug-in (critical)

// vue.config.js
module.exports = {
  transpileDependencies: ['u-draw-poster']};Copy the code

1. Create a poster drawing tool

<! -- #ifdef MP-WEIXIN -->
<canvas id="canvas" type="2d" style="width:100rpx; height:100rpx" />
<! -- #endif -->
<! -- #ifndef MP-WEIXIN -->
<canvas canvas-id="canvas" id="canvas" style="width:100rpx; height:100rpx" />
<! -- #endif -->
Copy the code
// error: '@/js_sdk/u-draw-poster'; // error: '@/js_sdk/u-draw-poster';
import DrawPoster from 'u-draw-poster'
async onReady() {
 // Initialize the drawing tool by passing in the selector (note that # is not needed). Type2d drawing will be automatically enabled when wechat applet is used
 const dp = await DrawPoster.build("canvas")}Copy the code

2. Set the canvas size

// Set the height of the rectangle to 100px
dp.canvas.width = 100
dp.canvas.height = 100
Copy the code

3. Draw anything

// Draw the background and text
dp.draw((ctx) = > {
    ctx.fillStyle = "#F4F4F4";
    ctx.fillRect(0.0, dp.canvas.width, dp.canvas.height);
    ctx.textBaseline = "top";
    ctx.textAlign = "start";
    ctx.fillStyle = "white";
    ctx.font = `bold The ${22}px sans-serif`;
    ctx.fillText('Mr Zhou', dp.canvas.width/2.38.5);
})
// Draw the image content
dp.draw(async (ctx) => {
    / /...
})
Copy the code

Note that the draw method automatically executes ctx.save/ctx.restore without manipulating the drawing stack.

dp.draw((ctx) = > {/ *... * /})
/ / equivalent to
ctx.save()
/ *... * /
ctx.restore()
Copy the code

4. Draw

Dp.draw does not draw immediately, but simply adds the task to the stack, using the dp.awaitCreate function, which pops up all tasks when it is drawn. Dp. awaitCreate In a non-2D drawing, the ctx.draw method is automatically executed after the drawing task is completed, and the draw drawing is asynchronous.

dp.draw((ctx) = > {/ *... * /})
dp.draw(async (ctx) => {/ *... * /})
// Since each task may have an asynchronous draw task, we need to await it with await
const result = await dp.awaitCreate();
// Successful drawing returns an array of drawing status for each task
console.log("Draw status :", result); // draw state: [true]
Copy the code

5. Generate the local address of the image

If you want to save as an image, you can use dp.createimgURL to create a local address for the image and save it in the WX or UNI API.

dp.draw(async (ctx) => {/ *... * /})
const result = await dp.awaitCreate();
const posterImgUrl = await dp.createImagePath();
console.log("Draw status :", result); // [true]
console.log("Draw generate local address :", posterImgUrl); / /... tmp...
Copy the code

You can also skip the dp.awaitCreate method and automatically check the list of tasks when calling dp.createImagePath and create the address after the drawing task if it exists.

dp.draw(async (ctx) => {/ *... * /})
// Skip drawposter.awaitCreate to generate the address directly
const posterImgUrl = await dp.createImagePath();
console.log("Draw generate local address :", posterImgUrl);
Copy the code

Draw extension API

When a drawPoster is created, extension methods are automatically added/overwritten to CTX (brushes) to build the poster rectangle.

dp.draw(async (ctx) => {
  // ctx.drawImage | ctx.drawRoundImage | ctx.fillWarpText | ....
})
Copy the code

DrawImage (ctx.drawImage)

ctx.drawImage(url, x, y, w, h)

DrawPoster is different from native drawing. CTX. DrawImage already has a built-in downloadFile and only needs to pass in the local/network address. Supports 2D and non-2D drawing in the same way. Need await to be drawn.

Note: When the drawing environment is H5, uniApp should not use large images when using local images for painting, otherwise it will fail to generate images.

dp.draw(async (ctx)=>{
    const url = "/static/logo.png"
    // const url = "https://...."
    await ctx.drawImage(url, 88.174.94.198.98.36);
})
Copy the code
parameter describe
url Network picture address, or local/staticImage path.
The x, y, The coordinates of the upper left corner of the picture.
Width, height The size of the picture.

Newline font (ctx.fillwarptext)

ctx.fillWarpText(options)

Pass in a configuration object and draw a newline font. The following are configurable items.

interface FillWarpTextOpts {
  // Draw a string, mandatory
  text: string;
  // Draw the maximum height, 100px by defaultmaxWidth? : number;// Draw the line height. Default is the default width of the current fontlineHeight? : number;// Draw the number of rows, the default limit is 2 layerslayer? : number;// Draw the X-axis, default 0x? : number;// Draw the Y-axis, default 0y? : number;/ / set the newline characters. The default is empty, such as setting, maxWidth | layer will be invalidatedsplitText? : string;// Do not draw immediatelynotFillText? : boolean; }// When 'notFillText' is' true ', no drawing is done and the function returns a draw queue
// Is used to represent the drawing information corresponding to each font line. Here is the structure information returned, which you can use to calculate
// The width of the newline font can also be drawn using array.forEach and ctx.fillText.[{text: string, y: number, x: number}
  / /...
]
Copy the code

Rounded rectangle (ctx.fillroundrect)

ctx.fillWarpText(x, y, w, h, r)

dp.draw(async (ctx)=>{
   // Set the rectangle color
   ctx.fillStyle = "#fff";
   // Draw
   ctx.fillRoundRect(15.179.345.365.5.10);
})
Copy the code
parameter describe
The x, y, Coordinates of the top left corner of the rectangle.
Width, height The size of the rectangle.
r Radians of the rectangle.

Rounded rectangle border (CTx.strokeroundrect)

ctx.strokeRoundRect(x, y, w, h, r)

parameter describe
The x, y, Coordinates of the top left corner of the rectangle.
Width, height The size of the rectangle.
r Radians of the rectangle.

Rounded corner image (CTx.drawroundimage)

ctx.drawRoundImage(url, x, y, w, h, r)

dp.draw(async (ctx) => {
  const url = "static/logo.png"
  // const url = "https://...."
  await ctx.drawRoundImage(url, 0.0.100.100.50);
});
Copy the code
parameter describe
url Network picture address, or local/staticImage path.
The x, y, The coordinates of the upper left corner of the picture.
Width, height The size of the picture.
r The radian radius of the image.

DrawQrCode (ctx.drawqrcode)

Generate two-dimensional code extension, the source code used uQRCode and changed, the file is relatively large, so as an extension plug-in, use to introduce the plug-in first.

// error: '@/js_sdk/u-draw-poster'; // error: '@/js_sdk/u-draw-poster';
import DrawPoster from 'u-draw-poster'
import drawQrCode from 'u-draw-poster/dist/extends/draw-qr-code'
// Introduce a plug-in to draw a QR code
DrawPoster.useCtx(drawQrCode)

async onReady() {
 const dp = await DrawPoster.build("canvas")
 dp.canvas.width = 200; dp.canvas.height = 200
 dp.draw(ctx= >{
   ctx.drawQrCode({
    x: (dp.canvas.width / 2) - 50.y: (dp.canvas.height / 2) - 50.text: "http://www.baidu.com".size: 100}); })}Copy the code
parameter type mandatory instructions
x number no Horizontal offset length
y number no Offset length in vertical direction
text String is Content of TWO-DIMENSIONAL code
size Number no Two-dimensional code size
margin Number no Margin, the actual size of the TWO-DIMENSIONAL code will be scaled according to the set margin value (default:0)
backgroundColor String no Background color, if set to transparent background,fileTypeShould be set to'png'And set the background color to'rgba(255,255,255,0)'(Default:'#ffffff')
foregroundColor String no Foreground (Default:'# 000000')
errorCorrectLevel Number no Error correction level, includingerrorCorrectLevel.L,errorCorrectLevel.M,errorCorrectLevel.Q,errorCorrectLevel.HFour levels,L: Up to 7% of errors can be corrected;M: Up to 15% of errors can be corrected;Q: up to 25% of errors can be corrected;H: Up to 30% of errors can be corrected.

Global instance API

Build (drawposter.build)

DrawPoster.build(string|object)

When the string is configured, the canvas of the string is queried directly. When the object is configured, object-selector is required. The following is the configuration item of options. Returns drawing the build object dp.

/** drawposter. build build configuration */
interface DrawPosterBuildOpts {
    // Query string (mandatory), be careful not to miswrite the corresponding canvas ID, no need to pass the # symbol
    selector: string;
    // Select the component rangecomponentThis? : any;// The type is 2D drawing, which is enabled by default and dynamically loaded in wechat applettype2d? : boolean;// Whether to display the loading box during the drawing processloading? : boolean,// When drawing a picture, wait for the drawing to complete (ms), only in AppdrawImageTime? :100.// Load prompt textloadingText? :'Drawing the poster... '.// Create image loading prompt textcreateText? :'Generate the picture... '
}
Copy the code

Multidrawing builds (drawposter.buildall)

DrawPoster.buildAll(Array<string|object>)

Build multiple drawing tools, to build function parameter string | options array that return multiple object composed of drawing tools. Key is canvasId and value is the build object.

Mount the global extension (drawposter.use)

DrawPoster.use(object)

Passing in the mount configuration object and adding the global extension method can generally be used to encapsulate the poster drawing template. When different pages have the same poster template, the amount of code can be effectively reduced. The usage is as follows.

1. Add extensions anywhere (recommended in main.js)

import DrawPoster from 'u-draw-poster'
// Global added extension implementation to draw personal posters
DrawPoster.use({
  name: "createMyCardImagePath".// dp is the current instance, and other parameters are custom incoming parameters
  handle: async (dp, opts) => {
    / /.. Custom build content..
    return await dp.createImagePath()
  }
})
Copy the code

Use custom extensions in pages

import DrawPoster from 'u-draw-poster'
async onReady() {
 const dp = await DrawPoster.build("canvas")
 dp.canvas.width = 100; dp.canvas.height = 100
 const posterImg = await dp.createMyCardImagePath({/ *... * /})}Copy the code

Mount the Draw extension (drawposter.usectx)

DrawPoster.useCtx(object)

Pass in the mount configuration object and add the global draw extension method to customize the definition of the draw method as follows.

1. Add extensions anywhere (recommended in main.js)

// Global add draw TWO-DIMENSIONAL code drawing extension implementation
DrawPoster.useCtx({
  name: "drawQrCode".Canvas (draw node), CTX (draw brush), the rest of the parameters are custom passed parameters
  handle: async (canvas, ctx, url, x, y, w, h) => {
    / /.. Custom draw content..}});Copy the code

Use custom extensions in drawing

dp.draw(ctx= > {
  const url = 'http://www.baidu.com'
  await ctx.drawQrCode(url, 0.0.50.50)})Copy the code

Draw a node (dp.canvas)

dp.canvas | dp.canvas.width | dp.canvas.height | ...

Dp. canvas is the global drawing root node and has an exclusive API in wechat applet. It will be used as a global width and height container on other ends. When dp. CreateImagePath incoming parameters, not default to dp. Canvas. Width | dp. Canvas. Height to create pictures, the following is a dp. API that exist in the canvas object and attributes.

interface Canvas {
  width: number;
  height: number;
  // The rest of the parameters are the wechat applet exclusive API, only the wechat applet has the API
  / / specific reference WeChat small program documentation: https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Canvas.html
}
Copy the code

Create draw (DP.draw)

dp.draw(async callback(ctx))

Renderer, receiving actuator function, added to the renderer container, can be modified as an asynchronous function to handle picture rendering, or can be a synchronous function.

Global Brush (DP.ctx)

dp.ctx

Global drawing brush. Special cases can be used. It is recommended to use only the dP. draw function for drawing.

Waiting to draw (dp.awaitCreate)

dp.awaitCreate()

Asynchronously draws the renderer stack, emptythe renderer container on success, and returns an array of stack status for success (Boolean []).

Stop painting (dp.stop)

dp.stop()

Stop the current drawing stack, call will cease to dp. AwaitCreate | dp. CreateImagePath execution.

Create an image (dp.createImagepath)

dp.createImagePath(options)

If the stack is not empty, dp.awaitCreate() will be called automatically to empty the stack. CreateImagePath creates the image based on canvas.width and canvas.height. If you want to customize the parameters, the awaitCreate method can take a configuration object and return the image address, as shown below.

interface CreateImagePathOptions { x? : number; y? : number; width? : number; height? : number; destWidth? : number; destHeight? : number; }Copy the code

Use advice

Canvas should be regarded as a generation tool in poster generation, and its function is only to draw posters. The generated resources should be saved and used, and the image image component should be used for display. The reason is that it is convenient for operation, such as adjusting size, or holding down the H5 end to save or identify, so canvas should put it in a place where it cannot be seen. Can’t use the display: none; overflow:hidden; Hide, otherwise blank is generated. The hidden style code of canvas is recommended here, which is the description provided by uQRCode, as well as u-draw-poster

.canvas-hide {
	/ * 1 * /
	position: fixed;
	right: 100vw;
	bottom: 100vh;
	/ * 2 * /
	z-index: -9999;
	/ * * / 3
	opacity: 0;
}
Copy the code

Q&A

Wechat mini program mobile phone browsing blank

If pictures are drawn, you need to add the downloadFile domain name in the background and restart the developer tool.

Micro channel small program can not be real machine debugging

Developers.weixin.qq.com/community/d…

H5 image clipping is abnormal

When two or more ctx.drawRoundImage rounded corner images are drawn on the H5 end, the local base64 created is displayed incorrectly. It is recommended that you limit rounded corner images to one when the H5 environment is used, or uni dynamically compilers display the IMG label.

No effect after drawing

Note that DrawPoster. Build does not check whether the canvasId you selected is correct, so make sure it is the same as canvas in canvas-ID and HTML. On the apet side, it will automatically switch to Type2D, so dynamic compilation must be added.

<! -- #ifdef MP-WEIXIN -->
<canvas id="canvas" type="2d" style="width: 300px; height: 300px" />
<! -- #endif -->
<! -- #ifndef MP-WEIXIN -->
<canvas canvas-id="canvas" id="canvas" style="width: 300px; height: 300px" />
<! -- #endif -->
Copy the code

Drawing multiple images is slow to load

If you feel that multiple picture drawing await is slow to load, you can use promise. all to synchronously draw part of the image that does not need to handle layer overlay.

dp.draw(async (ctx) => {
  // // User profile picture
  await ctx.drawRoundImage(headImgUrl, 39.790.90.90.100);
  await Promise.all([
    ctx.drawImage('/static/logo1.png'.20.20.35.35),
    ctx.drawImage('/static/tp.png'.19.86.612.459),
    ctx.drawImage('/static/bw.png'.188.559.274.50),
    // // user's QR code
    ctx.drawImage(codeImgUrl, 518.780.92.92),]); });Copy the code

Note that ctx.drawRoundImage cannot be placed in promise. all, because ctx.drawRoundImage will call ctx.clip internally, which will conflict with other image rendering in promise. all. This causes the fillet to fail.

My blog: Mr.Mao’ Blog

Contact: [email protected]