preface

Small program sharing can only be shared to friends or wechat group, can not be shared to the circle of friends.

This week received such a pit daddy demand, small program needs to achieve a picture sharing function. Users can send pictures to moments or other channels.

The first feeling is to throw the pot to the back end, call an interface and let the back end return a URL, the front end is only responsible for display. ^_^

Of course, such a naive idea is basically impossible to achieve, if you encounter such a backend, please cherish him.

Anyway, if the front end does this function by itself, it must use one thing, that is Canvas.

The early use

Canvas is not unique to small programs. It is also available in HTML5. But I really do not use much. There are only two times. One time, when I made h5 mini-game, the other time was very close to the demand of this time, which was also to put the content in a div into canvas and generate pictures for users to keep.

I will follow the previous ideas to do it is no problem, small program itself encapsulated a set of API, but canvas related API is basically the same as normal HTML, I will follow the previous Web way again.

Open with

Start by writing the following code in our WXML

<canvas class="canvas" canvas-id="canvas"></canvas>
Copy the code

The Canvas size can be set directly on the Canvas tag in WXML or using WCSS. Personally, I prefer WCSS.

The important point is that Canvas-id is unique to the small program and is the unique identifier of canvas component, which will be needed in many places later.

And then we start to write JS

// Canvas is the canvas id in WXML
let ctx = wx.createCanvasContext('canvas')
Copy the code

The first pit place came, normal to page is no problem, but in the component of the words, can not draw, is not displayed.

A search of the document will bring up the following passage

Create the Canvas drawing context (specify canvasId). Under the custom component, the second argument is passed to the component instance this to operate on the < Canvas /> component within the component.

Obviously, we can just change the code to the following

let ctx = wx.createCanvasContext('canvas'.this)
Copy the code

Draw text and pictures in the appropriate position

I’m not going to tell you how to draw it, but I’m going to tell you where the pits are.

In the corresponding position

Yeah, just one really bad spot. The canvas is centered and points are positioned differently on different screen sizes. We need to get the width and height of the screen for calculation. The (mainly width) applet provides the following aspects to get some information about the device

wx.getSystemInfo({
  success: (res) = > {
    this.setData({
      windowWidth: res.windowWidth,
      windowHeight: res.windowHeight,
    })
  }
})
Copy the code

The text

This is the worst point

If you look at the UI, there’s a two-line paragraph in the middle.

When I first got it, I thought it was so easy. Count the number of words per line. It’s not good to cut two paragraphs. The end result is really bad. Some are long, some are short, and some are beyond the limit.

This obviously doesn’t meet our needs. And that’s what caused the problem. Because our content is mixed in Chinese and English, Chinese takes two characters and English only takes one. When we cut it according to the length of the string, both Chinese and English are 1. Alignment can only be done in pure English or Chinese. How do you solve it? The code!

getContent(detail, length = 24, row = 2) {
  let len = 0
  let index = 0
  let content = []
  for (let i = 0; len < detail.length; i++) {
    // if not defined, then ""
    if(! content[index]) content[index] =' '
    content[index] += detail[i]
    // Chinese characters or digits have two lengths
    if (detail.charCodeAt(i) > 127 || (detail.charCodeAt(i) >= 48 && detail.charCodeAt(i) <= 57)) {
      len += 2;
    } else {
      len++;
    }
    if (len >= length || (row - index == 1 && len >= length - 2)) {
      len = 0
      index++
    }
    if (index === row) break
  }
  return content
}
Copy the code

We can use charCodeAt to get charCode, which is above 127 in Chinese (ps). When I tested it, I found that the number was also two lengths). So in case there’s a change in requirements later on I’m going to pull this out and write a method. Then you just pass in the content, the number of words per line and the number of lines.

The picture

There are two pits here

  1. Can’t use the network picture directly, can’t draw on the real machine
  2. No rounded corners. Images that are square will not be processed

Use of Network pictures

Since we can not directly use the network picture, so we change the idea, download the picture to the local, in the display is not on the line.

  1. Download network pictures
wx.getImageInfo({
  src: url,
  success: (res) = > {
    // You can obtain the local path after the download is successful
    console.log(res.path)
  }
})
Copy the code

If you just use this, you’ll see that it’s going to make a mistake. Small program to download the image to do whitelist processing. You need to set the domain name of the network picture in the miniprogram background > Settings > Server domain name > downloadFile legal domain name.

Ps. Because the domain name requirements are HTTPS, and can only be changed five times a month, it is recommended to download the network image on your HTTPS server, and then go through the CDN.

  1. Draw pictures

Next do the same as drawing local images

ctx.drawImage(res.path, left, top, width, height);
Copy the code

Image fillet processing (this example demonstrates circle, same principle)

The whole idea is to draw a circular area, then draw the picture in this area, and finally merge and intercept to generate a circular image. The specific code is shown below

drawImage(ctx, url, left, top, width, height) {
  // Save the state of the current environment
  ctx.save();
  // Start a path or reset the current path
  ctx.beginPath();
  // Draw a circle
  ctx.arc(width / 2 + left, height / 2 + top, width / 2.0.Math.PI * 2.false);
  // Cut areas of arbitrary shape and size from the original canvas
  ctx.clip();
  wx.getImageInfo({
    src: url,
    success: (res) = > {
      // Draw an image to the canvas
      ctx.drawImage(res.path, left, top, width, height);
      // Returns the previously saved path status and properties
      ctx.restore();
      / / drawctx.draw(); }})},Copy the code

It will be found that downloading images is an asynchronous operation. If only one image is available, it will be very messy if there are multiple images. We can use Promise to optimize

drawImage(ctx, url, left, top, width, height) {
  ctx.save();
  ctx.beginPath();
  ctx.arc(width / 2 + left, height / 2 + top, width / 2.0.Math.PI * 2.false);
  ctx.clip();
  return new Promise((resolve, reject) = > {
    wx.getImageInfo({
      src: url,
      success: (res) = > {
        ctx.drawImage(res.path, left, top, width, height);
        ctx.restore();
        resolve()
      },
      fail: (e) = > {
        reject(e)
      }
    })
  })
},
Copy the code

Exports the contents of the specified area of the current canvas to generate a picture of the specified size, and returns the file path.

Use the canvasToTempFilePath provided by wechat

savePic () {
  let that = this;
  let offset_left = (this.data.windowWidth - 303) / 2
  console.log('savePic')
  wx.canvasToTempFilePath({
    x: offset_left,
    y: 0.width: 303.height: 398.canvasId: 'canvas'.success: function (res) {
      console.log(res.tempFilePath)
    },
    fail (e) {
      console.log(e)
    }
  }, this)}Copy the code

Preview/save images

In the previous step we saved the contents of the canvas to generate the image. There are actually two ways to go about it.

  1. SaveImageToPhotosAlbum, save the file to an album
  2. PreviewImage: Previews the image directly

In fact, both methods can be used according to the actual needs of the corresponding choice.

I used previewImage in the selection, because this does not require user authorization, users can directly send the image to their friends without saving the image. If you want to save the image, you can also save it during the preview.

Little sense

I used to feel that the article felt that it was so, when I wrote, I found out how difficult it was. Maybe not. Keep trying. This is my third post on the Nuggets

The first two are below, if you need them

Small program pit filling tips network request transformation mobile end adaptation problem solution

Recently writing a DApp, based on Nebulas public link. There is an incentive plan for two months from May to July. If you put one DApp on the shelf, you can get 110 NAS, which is about 6000 RMB, and the weekly prize is about 100W RMB. However, this is basically ignored, but putting one DApp on the shelf is also a considerable income. Interested partners can go to register. Register link

In the first week, I submitted two DApps, but I met some difficulties. I will write a tutorial this week to help you develop DApp better