In the first article of 2020, I was busy reviewing and brushing questions at the beginning of the year and had no time to write anything. The more I read, the more INFERIOR MY skills became, and I was always wandering in the ranks of small fish. Recently, I happened to have a canvas business in the project, which suddenly ignited my fire for a UI front end.

On the pit 💥

Question 1: Why blur images on canvas?

When drawing pictures/text on canvas, if we set the width and height of Canvas: 375*667, we will find that the drawn picture is very blurred and feels like a picture with poor resolution, and the text will also look overlapping.

Note: The physical pixel is the smallest unit displayed on the phone screen, while the device-independent pixel (logical pixel) is a point in the computer device, and the pixel set in the CSS refers to that pixel.

Reason: In front-end development we know of a property called devicePixelRatio that determines how many (usually 2) physical pixels will be used to render a device-specific pixel in an interface.

For example, a 100*100 pixel image will use 2 pixels to render one pixel of the image on the Retina screen, which is equivalent to doubling the image size, so the image will become blurred, which is also the reason why 1px becomes thick on the Retina screen.

Solution: Enlarge both canvas-width and canvas-height by 2x, and then use style to reduce the display width and height of canvas by 2x.

Such as:

<canvas width="320" height="180" style="width:160px; height:90px;"></canvas>
Copy the code

Question 2: How to convert PX to RPX?

RPX is a small applet specific unit of size that ADAPTS to the width of the screen, and on the iphone6/iphonex, 1rpx equals a different px. Therefore, it is likely that your canvas display will be inconsistent between different phones.

Before drawing the poster, we usually get the design draft based on iphone6 2x. Besides, from the solution of the previous problem, we know that the size of canvas is also twice, so we can directly measure and draw the design draft of the 2x diagram, and pay attention to the size of rpxtoPx.

/** ** @param {*} RPX * @param {*} int // whether to become integer factor => 0.5 //iphone6 pixelRatio => 2 */ toPx(RPX, int) {if (int) {
      return parseInt(rpx * this.factor * this.pixelRatio)
    }
    return rpx * this.factor * this.pixelRatio
  }
Copy the code

Question 3: about canvasContext measureText computing time of pure digital mobile phones is 0

Provide this.ctx.measureText(text).width in the applet to calculate the length of text, but if you use all numbers, you’ll find that the API always evaluates to 0. Finally, a simulated measureText method is used to calculate the text length.

measureText(text, fontSize = 10) {
    text = String(text)
    text = text.split(' ')
    let width = 0
    text.forEach(function(item) {
      if (/[a-zA-Z]/.test(item)) {
        width += 7
      } else if(/[0-9]/.test(item)) {width += 5.5}else if(/\./.test(item)) {width += 2.7}else if(/-/.test(item)) {width += 3.25}else if(/ [\ u4e00 - \ u9fa5] /. The test (item)) {/ / Chinese match width + = 10}else if(/ \ | \)/test (item)) {width + = 3.73}else if(/\s/.test(item)) {width += 2.5}else if (/%/.test(item)) {
        width += 8
      } else {
        width += 10
      }
    })
    return width * fontSize / 10
  }
Copy the code

Q4: How do I center a line of fonts? How line?

If the font is too long, it will exceed the canvas, resulting in ugly drawing. In this case, we should make the excess part become… You can set a width and loop to calculate the width of the text. If it exceeds, add it with subString. Can.

let fillText=' '
let width = 350
for (leti = 0; i <= text.length - 1; I ++) {fillText = fillText + text[I] // determine the truncation positionif (this.measureText(fillText, this.toPx(fontSize, true)) >= width) {
          if (line === lineNum) {
            if(i ! == text.length - 1) { fillText = fillText.substring(0, fillText.length - 1) +'... '}}if (line <= lineNum) {
            textArr.push(fillText)
          }
          fillText = ' '
          line++
        } else {
          if (line <= lineNum) {
            if (i === text.length - 1) {
              textArr.push(fillText)
            }
          }
        }
      }
Copy the code

The text play shows the calculation formula:

In canvas, you can use (Canvas width – text width) /2 + x (x is the X-axis of the font).

let w = this.measureText(text, this.toPx(fontSize, true))
this.ctx.fillText(text, this.toPx((this.canvas.width - w) / 2 + x), this.toPx(y + (lineHeight || fontSize) * index))

Copy the code

Question 5: How do network diagrams work in applets?

As for the use of network pictures in small programs, such as pictures on CDN, it is necessary to go down to wechat local for LRU management, so as to save download time when the same pictures are drawn later. Therefore, first of all, you need to configure the legal domain name downloadFile in the background of wechat mini program. Secondly, before drawing the canvas, you’d better go down the picture in advance, wait for the picture to download, and then start drawing, so as to avoid some problems of drawing failure.

Q6: You can set the base64 image data to draw in the IDE, but it is useless on the real machine?

First convert base64 to Uint8ClampedArray format. Wx.canvasputimagedata (OBJECT, this) is then used to draw on the canvas and export the canvas as a picture.

Question 7: About WX. canvasToTempFilePath

After successful drawing with Canvas, you can directly call this method to generate pictures. There is no problem on IDE, but the generated pictures may be incomplete on the real machine. You can use a setTimeout to solve this problem.

this.ctx.draw(false, () = > {setTimeout(() => {
            Taro.canvasToTempFilePath({
              canvasId: 'canvasid', success: Async (res) => {this.props. OnSavePoster (res.tempFilepath)// Callback event // Empty canvas this.ctx.clearRect(0, 0, canvas_width, canvas_height) }, fail: (err) => { console.log(err) } }, this.$scope)
          }, time)
    })
Copy the code

Question 8: About canvascontext.font

Setting the fontsize portion of a font to include decimals invalidates the entire font setting.

Question 9: Font rendering mismatch under Android?

this.ctx.setTextAlign(textAlign)

Problem 10: Draw a line chart

To draw a simple line graph on canvas, you only need to join points using the lineTo and moveTo apis. Draw the shadow using createLinearGradient.

Thinking 💡

Thought 1: The limitations of using JSON configuration tables to generate posters

Now the poster generation only needs to measure the size in accordance with the design draft, but the process of measure is still very cumbersome, in the design draft is less than the place also need to manually fine-tune. Follow-up can also do a Web side use drag and drop to complete the design of the work, automatically generated JSON application to the small program poster.

Thought 2: The limitations of back-end poster generation

At the beginning, the poster was generated by the students at the back end. The advantage is that there is no front-end drawing time and no need to step on the pit of wechat API. The interface returns and gets the URL to display, but the effect generated at the back end is not good, after all, this work is more front-end.

Thought 3: The limitations of front-end poster generation

When the front end generates posters, I find it takes longer, including the local download of pictures, and I also need to write a setTimeout for Android to ensure the normal drawing. Various compatibility issues, mobile DPR, Android and ios, etc. Continuous egg stepping on your head ~ hahaha ~

eggs

Using the latest Canvas-2D background can not draw all?

During the development of Canvas, there was always a glimmer of light in the small program to remind me.

I also tried the latest CANvas2D API, it does sync to the Web side, and the writing style is smoother, everything looks fine in the developer tools, running on the phone, it only shows half the width and testing on various models is the same.

Change the back to the original canvas… The specific reason has not been found in the wechat community, so we will study it in the next iteration and upgrade.