Antecedents to review

I published in the earlier article “how to achieve micro channel small program change avatar? Three steps to help you fix!” , mentioned that there are three steps to achieve wechat mini program to change the avatar:

  1. Get user profile picture
  2. Image template
  3. Image synthesis

The previous article has been on the user profile picture and image template two steps to explain, this article will elaborate on how to synthesize images.

A very important part of the picture composition process is to cut the picture. This function point is very fixed, mostly after dragging and zooming the picture, cut out a fixed length and width picture in a certain area. This kind of function in THE APP end and H5 have a lot of mature plug-ins for use, next to see me in the dolphin interesting picture small program in the head cut plug-in is how to achieve, welcome your comments.

To better understand the following code, I recommend that you scan and experience the cropping effect of the image.

Implementation effect

Interface implementation

Dragging and zooming images in H5 requires a bunch of code, and there are many implementations on the web. Small programs are much easier to implement, with

and

<view class="clip-view">
  <! ImgHeight, imgWidth imgUrl is the url of the clipped image -->
  <movable-area class="moveare" style="height: {{clipHeight}}rpx; width: {{clipWidth}}rpx; ">
    <movable-view scale="true" scale-min="{{1}}" damping="1000" style="height: {{imgHeight}}px; width: {{imgWidth}}px; " direction="all" x="{{x}}" y="{{y}}" bindchange="_onChange" bindscale="_onScale">
      <image class="clip-img" src="{{imgUrl}}" />
    </movable-view>
  </movable-area>
  <! -- Trim box decoration -->
  <view class="clip-box" style="height: {{clipHeight}}rpx; width: {{clipWidth}}rpx; ">
    <! -- Clipbox four corners -->
    <view class="clip-border clip-border-lt"></view>
    <view class="clip-border clip-border-rt"></view>
    <view class="clip-border clip-border-lb"></view>
    <view class="clip-border clip-border-rb"></view>
  </view>
  <! -- Canvas for clipping images -->
  <canvas class="clip-canvas" id="img_clip_canvas" canvas-id="img_clip_canvas" style="height: {{clipHeight}}rpx; width: {{clipWidth}}rpx; "></canvas>
</view>
Copy the code

Component attributes

The input parameter to the component only needs the address of the original image and the width and height of the image clipbox

/** * Component property list */
properties: {
  // The path of the original image (the image to cut)
  imgUrl: {
    type: String.value: ' '
  },
  // The width of the clip (RPX)
  clipWidth: {
    type: Number.value: 500
  },
  // Cut height (RPX)
  clipHeight: {
    type: Number.value: 500}}Copy the code

The component data

The component’s data will record more data about the drag and zoom of the picture. It should be noted that the initial position of the picture and the drag position are not stored in the same variable, in order to prevent jitter in the drag process.

/** * The initial data of the component */
data: {
  baseScale: 1.imgPath: ' '.imgWidth: 0./ / picture width
  imgHeight: 0./ / picture
  x: 0.// The image starts with the X-axis position
  y: 0.// The y position of the image does not share the same variable as top, because if y value is changed frequently, the image will flicker, the same as x
  left: 0.// Drag the image to the X-axis position
  top: 0.// Drag the image to the Y-axis position
  scale: 1 // Zoom after drag and drop
}
Copy the code

Component initialization

The component’s initialization method is used to scale the image to the appropriate size and center the clipbox

/** * Initialization method * After obtaining the width and height of the image, scale the image to the size of the clipbox, * and place the clipbox in the center of the image **/
_init() {
  if (!this.data.imgUrl) return
  // Get the screen width
  let {
    screenWidth
  } = wx.getSystemInfoSync()
  // Calculate screen RPX
  const rpx = screenWidth / 750
  // Get the image width and height, then scale to the clipbox size
  wx.getImageInfo({
    src: this.data.imgUrl,
    success: ({
      width,
      height,
      path
    }) => {
      const cw = this.data.clipWidth * rpx
      const ch = this.data.clipHeight * rpx
      let scale = Math.max(cw / width, ch / height)
      const imgWidth = width * scale
      const imgHeight = height * scale
      this.setData({
        imgPath: path,
        imgWidth,
        imgHeight,
        baseScale: scale,
        x: (cw - imgWidth) / 2.y: (ch - imgHeight) / 2})}})}Copy the code

Picture drag and zoom processing

After initialization, you can monitor the user’s drag and zoom operations, mainly recording the position of drag and zoom ratio, specific to the code implementation is to monitor the bindchange and bindscale events of

  // Drag the event response function
  _onChange: function(e) {
    this.setData({
      x: e.detail.x,
      y: e.detail.y
    })
  }

  // Scale the event response function
  _onScale: function(e) {
    this.setData({
      x: e.detail.x,
      y: e.detail.y,
      scale: e.detail.scale
    })
  }
Copy the code

Image clipping implementation

After dragging and zooming, it will be clipped. Clipping is to redraw the clipping area of the picture using

, save it in wechat temporary directory, and return to the saving path. It should be noted that the image clipping position recorded after dragging and zooming is not the position of the original image. When drawing with canvas drawImage, the position of the original image should be converted to, or the coordinate system of canvas should be zoomed first. Note: When a custom component calls the wx.createcanvasContext (String canvasId, Object this) method, the second parameter this cannot be omitted, otherwise the Canvas will not respond

/** ** image clipping entry method */
clip() {
  const scale = this.data._scale * this.data._baseScale
  const canvasId = 'img_clip_canvas'
  imageClip(canvasId, this.data.imgPath, {
    x: this.data._left * - 1.y: this.data._top * - 1,
    scale,
    width: this.data.clipWidth,
    height: this.data.clipHeight
  }, this)}** @param canvas canvas component id, Used to draw the clipped image * @param img The image to be clipped * @param option.left Clipped image left margin * @param option.top Clipped image top margin * @param option.left clipped image left margin * @param option.top Clipped image top margin * @param option.width Cut image width * @param option.height Cut image height * @param context component instance object * * @return new Promise(resolve=> CTX) * /
imageClip(canvas, img, option, context) {
  return new Promise((resolve, reject) = > {
    option = Object.assign({
      left: 0.top: 0.scale: 1.width: 0.height: 0
    }, option)
    let x = option.left / option.scale
    let y = option.top / option.scale
    let clipW = option.width / option.scale
    let clipH = option.height / option.scale
    const ctx = wx.createCanvasContext(canvas, context)
    ctx.drawImage(img,
      x,
      y,
      clipW,
      clipH,
      0.0,
      option.width,
      option.height)

    ctx.draw(false, (e) => {
      console.log(e)
      resolve()
    })

  })
},

/** * Canvas is saved as temporary file */
function saveCanvasToTemp(canvas, option) {
  return new Promise((resolve, reject) = >{ wx.canvasToTempFilePath({ ... option,canvasId: canvas,
      success:resolve,
      fail:reject
    }, this)})}Copy the code

Write in the last

This is the main code implementation of the small program version of the image clipping, there are a few minor points to note

  • <movable-view>Set the value of damping to uppercase, otherwise it may be dragged out of bounds
  • Call in custom componentswx.createCanvasContext(string canvasId, Object this)Method, the second parameter this cannot be omitted, otherwise the canvas will not respond
  • Remember to convert to the actual size of the image when cutting

About us

The front end team of Kuaigou Taxi focuses on sharing front-end technology and regularly pushes high-quality articles, which are welcome to be praised. This article will be published on our official account, just scan it!