PK Creative Spring Festival, I am participating in the “Spring Festival Creative Submission contest”, please see:Spring Festival creative Submission contest

preface

Recently leopard is very hot, we began to change the avatar, I wish you 2022 bonanza, promotion and salary ha ~ I follow this popular direction to do a small program to change the avatar, you can change the way you want, and others will not have the same avatar, WE are programmers will be different

First, use wechat mini program to obtain the user’s profile picture

This can be used directly from the code that I gave you when I created a new applet, so I don’t need the backend interface to fetch it and store it in the cache. The trouble here is that the avatars I get are small images, so it’s going to be very fuzzy, so I need to get a high definition avatars. The applet has its own configuration to get the avatars size

<block wx:if="{{! HasUserInfo}"> <button wx:if="{{canIUseGetUserProfile}}" bindTap ="getUserProfile"> <button wx:else </button> </block> </view> js,data: HeadImg: "/ / avatar canvasBg:", ".. /.. /assets/images/canvasBg.png", userInfo: {}, hasUserInfo: false, canIUseGetUserProfile: false, js:method: GetUserProfile (e) {// It is recommended to use wx.getUserProfile to obtain user information. Developers need user confirmation every time to obtain user personal information through this interface. Wx. getUserProfile({desc: 'for improving member information ', // declare the purpose of obtaining user personal information, it will be displayed in the subsequent popup, please fill in success carefully: (res) => { this.setData({ userInfo: res.userInfo, hasUserInfo: true }) this.changeBgToHeadImg(); }})}, getUserInfo(e) {// It is not recommended to use getUserInfo to obtain user information. It is expected that from April 13, 2021, getUserInfo will no longer pop up pop-ups. This.setdata ({userInfo: e.dail.userinfo, hasUserInfo: true}) this.changebgToheadimg (); },Copy the code

Two, prepare some pictures that can be dressed up again

  • I take screenshots of some nice pictures and then use PHOTOSHOP to cut them into transparent colors
  • They then upload the image to their own server
  • Define an array in js data and display it directly on the page to reduce the additional cost of calling the interface

3. Find a drag-and-drop component that can save pictures to the canvas

This component is directly found on the Internet. You can search canvas-Drag and import it into the JSON file of the page

<canvas canvas-id='canvas-label' 
disable-scroll="true"
bindtouchstart="start"
bindtouchmove="move"
bindtouchend="end"
 style='width: {{width}}rpx; height: {{height}}rpx; '></canvas>
Copy the code
// components/canvas-drag/index.js
const DELETE_ICON = './icon/close.png'; // Delete button
const DRAG_ICON = './icon/scale.png'; // Zoom button
const STROKE_COLOR = 'red';
const ROTATE_ENABLED = true;
let isMove = false; // Indicates whether there is movement after touching, which is used to determine whether the operation history needs to be added
var isExport = false;// Indicates whether to export

const DEBUG_MODE = false; // Debug will render the action area border (effective without background)
const dragGraph = function ({x = 30, y = 30, w, h, type, text, fontSize = 20, color = 'red', url = null, rotate = 0, sourceId = null, selected = true}, canvas, factor) {
    if (type === 'text') {
        canvas.setFontSize(fontSize);
        const textWidth = canvas.measureText(text).width;
        const textHeight = fontSize + 10;
        this.centerX = x + textWidth / 2;
        this.centerY = y + textHeight / 2;
        this.w = textWidth;
        this.h = textHeight;
    } else {
        this.centerX = x + w / 2;
        this.centerY = y + h / 2;
        this.w = w;
        this.h = h;
    }

    this.x = x;
    this.y = y;

    // 4 vertex coordinates
    this.square = [
        [this.x, this.y],
        [this.x + this.w, this.y],
        [this.x + this.w, this.y + this.h],
        [this.x, this.y + this.h]
    ];

    this.fileUrl = url;
    this.text = text;
    this.fontSize = fontSize;
    this.color = color;
    this.ctx = canvas;
    this.rotate = rotate;
    this.type = type;
    this.selected = selected;
    this.factor = factor;
    this.sourceId = sourceId;
    this.MIN_WIDTH = 20;
    this.MIN_FONTSIZE = 10;
};

dragGraph.prototype = {
    /**
     * 绘制元素
     */
  paint() {
        this.ctx.save();
        // Because measureText gets the text width depending on the style, the text element needs to be styled first
        let textWidth = 0;
        let textHeight = 0;
        if (this.type === 'text') {
            this.ctx.setFontSize(this.fontSize);
            this.ctx.setTextBaseline('middle');
            this.ctx.setTextAlign('center');
            this.ctx.setFillStyle(this.color);
            textWidth = this.ctx.measureText(this.text).width;
            textHeight = this.fontSize + 10;
            // The center of the font area remains unchanged, and the upper left corner is shifted
            this.x = this.centerX - textWidth / 2;
            this.y = this.centerY - textHeight / 2;
        }

        // Rotate the element
        this.ctx.translate(this.centerX, this.centerY);
        this.ctx.rotate(this.rotate * Math.PI / 180);
        this.ctx.translate(-this.centerX, -this.centerY);
        // Render elements
        if (this.type === 'text') {
            this.ctx.fillText(this.text, this.centerX, this.centerY);
        } else if (this.type === 'image') {
          if (isExport) {
            console.log(this.y);
            if (this.y<=0) {
              this.ctx.drawImage(this.fileUrl, this.x, this.y, this.w, this.h-20);
            } else if (this.y >= 160 && this.y < 210) {
              this.ctx.drawImage(this.fileUrl, this.x, this.y-36.this.w, this.h-30);
            } else if ((this.y >= 210)) {
              this.ctx.drawImage(this.fileUrl, this.x, this.y - 36.this.w, this.h - 30);
            } else if (this.y >= 160 && this.y > 210) {
              this.ctx.drawImage(this.fileUrl, this.x, this.y-5.this.w, this.h - 30);
            } else if ((this.y >=60)) {
              this.ctx.drawImage(this.fileUrl, this.x, this.y-12.this.w, this.h-30);
            } else {
              console.log(this.x);
              this.ctx.drawImage(this.fileUrl, this.x, this.y-8.this.w, this.h-30);
            }
            //this.ctx.drawImage(this.fileUrl, this.x, this.y, this.w, this.h);
          }else {
            this.ctx.drawImage(this.fileUrl, this.x, this.y, this.w, this.h); }}// If it is selected, draw select dotted box, and zoom icon, delete icon
      if (this.selected) {
            this.ctx.setLineDash([2.5]);
            this.ctx.setLineWidth(2);
            this.ctx.setStrokeStyle(STROKE_COLOR);
            this.ctx.lineDashOffset = 6;

            if (this.type === 'text') {
                this.ctx.strokeRect(this.x, this.y, textWidth, textHeight);
                this.ctx.drawImage(DELETE_ICON, this.x - 15.this.y - 15.30.30);
                this.ctx.drawImage(DRAG_ICON, this.x + textWidth - 15.this.y + textHeight - 15.30.30);
            } else {
                this.ctx.strokeRect(this.x, this.y, this.w, this.h);
                this.ctx.drawImage(DELETE_ICON, this.x - 15.this.y - 15.30.30);
                this.ctx.drawImage(DRAG_ICON, this.x + this.w - 15.this.y + this.h - 15.30.30); }}this.ctx.restore();
    },
    /** * Stroke the rectangle *@private* /
    _drawBorder() {
        let p = this.square;
        let ctx = this.ctx;
        this.ctx.save();
        this.ctx.beginPath();
        ctx.setStrokeStyle('orange');
        this._draw_line(this.ctx, p[0], p[1]);
        this._draw_line(this.ctx, p[1], p[2]);
        this._draw_line(this.ctx, p[2], p[3]);
        this._draw_line(this.ctx, p[3], p[0]);
        ctx.restore();
    },
    /** * draw a line *@param ctx
     * @param a
     * @param b
     * @private* /
    _draw_line(ctx, a, b) {
        ctx.moveTo(a[0], a[1]);
        ctx.lineTo(b[0], b[1]);
        ctx.stroke();
    },
    /** * Determine where the click coordinates fall *@param {*} X Click coordinates *@param {*} Y click the coordinate */
    isInGraph(x, y) {
        // Remove the coordinates in the upper left corner of the region and the height width of the region
        const delW = 30;
        const delH = 30;

        // Delete region coordinates after rotation
        const transformedDelCenter = this._rotatePoint(this.x, this.y, this.centerX, this.centerY, this.rotate);
        const transformDelX = transformedDelCenter[0] - delW / 2;
        const transformDelY = transformedDelCenter[1] - delH / 2;

        // Transform the coordinates of the upper left corner of the region and the height width of the region
        const scaleW = 30;
        const scaleH = 30;
        const transformedScaleCenter = this._rotatePoint(this.x + this.w, this.y + this.h, this.centerX, this.centerY, this.rotate);

        // Transform region coordinates after rotation
        const transformScaleX = transformedScaleCenter[0] - scaleW / 2;
        const transformScaleY = transformedScaleCenter[1] - scaleH / 2;

        // For debugging, identify the operable area
        if (DEBUG_MODE) {
            // Identifies the delete button area
            this.ctx.setLineWidth(1);
            this.ctx.setStrokeStyle('red');
            this.ctx.strokeRect(transformDelX, transformDelY, delW, delH);
            // Identifies the rotate/scale button area
            this.ctx.setLineWidth(1);
            this.ctx.setStrokeStyle('black');
            this.ctx.strokeRect(transformScaleX, transformScaleY, scaleW, scaleH);
            // Identify the moving area
            this._drawBorder();
        }

        if (x - transformScaleX >= 0 && y - transformScaleY >= 0 &&
            transformScaleX + scaleW - x >= 0 && transformScaleY + scaleH - y >= 0) {
            // Scale the area
            return 'transform';
        } else if (x - transformDelX >= 0 && y - transformDelY >= 0 &&
            transformDelX + delW - x >= 0 && transformDelY + delH - y >= 0) {
            // Delete the region
            return 'del';
        } else if (this.insidePolygon(this.square, [x, y])) {
            return 'move';
        }
        // Not in the selected area
        return false;
    },
    /** * determine if a point is inside a polygon *@param Points Polygon coordinate set *@param TestPoint testPoint coordinates * returns true and false * */
    insidePolygon(points, testPoint) {
        let x = testPoint[0], y = testPoint[1];
        let inside = false;
        for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
            let xi = points[i][0], yi = points[i][1];
            let xj = points[j][0], yj = points[j][1];

            letintersect = ((yi > y) ! = (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);if(intersect) inside = ! inside; }return inside;
    },
    /** * Calculates the coordinates of the four vertices of the rotated rectangle (relative to the canvas) *@private* /
    _rotateSquare() {
        this.square = [
            this._rotatePoint(this.x, this.y, this.centerX, this.centerY, this.rotate),
            this._rotatePoint(this.x + this.w, this.y, this.centerX, this.centerY, this.rotate),
            this._rotatePoint(this.x + this.w, this.y + this.h, this.centerX, this.centerY, this.rotate),
            this._rotatePoint(this.x, this.y + this.h, this.centerX, this.centerY, this.rotate),
        ];
    },
    /** * Calculates the rotated new coordinates (relative to the canvas) *@param x
     * @param y
     * @param centerX
     * @param centerY
     * @param degrees
     * @returns {x} []
     * @private* /
    _rotatePoint(x, y, centerX, centerY, degrees) {
        let newX = (x - centerX) * Math.cos(degrees * Math.PI / 180) - (y - centerY) * Math.sin(degrees * Math.PI / 180) + centerX;
        let newY = (x - centerX) * Math.sin(degrees * Math.PI / 180) + (y - centerY) * Math.cos(degrees * Math.PI / 180) + centerY;
        return [newX, newY];
    },
    / * * * *@param {*} The coordinates of the px finger *@param {*} Py The coordinates of the finger *@param {*} X the coordinate to which the finger moves *@param {*} Y is the coordinate that my finger moves to *@param {*} CurrentGraph Information for current layer */
    transform(px, py, x, y, currentGraph) {
        // Gets the width and height of the selected area
        if (this.type === 'text') {
            this.ctx.setFontSize(this.fontSize);
            const textWidth = this.ctx.measureText(this.text).width;
            const textHeight = this.fontSize + 10;
            this.w = textWidth;
            this.h = textHeight;
            // The center of the font area remains unchanged, and the upper left corner is shifted
            this.x = this.centerX - textWidth / 2;
            this.y = this.centerY - textHeight / 2;
        } else {
            this.centerX = this.x + this.w / 2;
            this.centerY = this.y + this.h / 2;
        }

        const diffXBefore = px - this.centerX;
        const diffYBefore = py - this.centerY;
        const diffXAfter = x - this.centerX;
        const diffYAfter = y - this.centerY;

        const angleBefore = Math.atan2(diffYBefore, diffXBefore) / Math.PI * 180;
        const angleAfter = Math.atan2(diffYAfter, diffXAfter) / Math.PI * 180;

        // The rotation Angle
        if (ROTATE_ENABLED) {
            this.rotate = currentGraph.rotate + angleAfter - angleBefore;
        }

        const lineA = Math.sqrt(Math.pow((this.centerX - px), 2) + Math.pow((this.centerY - py), 2));
        const lineB = Math.sqrt(Math.pow((this.centerX - x), 2) + Math.pow((this.centerY - y), 2));
        if (this.type === 'image') {
            let resize_rito = lineB / lineA;
            let new_w = currentGraph.w * resize_rito;
            let new_h = currentGraph.h * resize_rito;

            if (currentGraph.w < currentGraph.h && new_w < this.MIN_WIDTH) {
                new_w = this.MIN_WIDTH;
                new_h = this.MIN_WIDTH * currentGraph.h / currentGraph.w;
            } else if (currentGraph.h < currentGraph.w && new_h < this.MIN_WIDTH) {
                new_h = this.MIN_WIDTH;
                new_w = this.MIN_WIDTH * currentGraph.w / currentGraph.h;
            }

            this.w = new_w;
            this.h = new_h;
            this.x = currentGraph.x - (new_w - currentGraph.w) / 2;
            this.y = currentGraph.y - (new_h - currentGraph.h) / 2;

        } else if (this.type === 'text') {
            const fontSize = currentGraph.fontSize * ((lineB - lineA) / lineA + 1);
            this.fontSize = fontSize <= this.MIN_FONTSIZE ? this.MIN_FONTSIZE : fontSize;

            // Recalculate the coordinates after rotation
            this.ctx.setFontSize(this.fontSize);
            const textWidth = this.ctx.measureText(this.text).width;
            const textHeight = this.fontSize + 10;
            this.w = textWidth;
            this.h = textHeight;
            // The center of the font area remains unchanged, and the upper left corner is shifted
            this.x = this.centerX - textWidth / 2;
            this.y = this.centerY - textHeight / 2; }},toPx(rpx) {
        return rpx * this.factor; }}; Component({/** * Component property list */
    properties: {
        graph: {
            type: Object.value: {},
            observer: 'onGraphChange',},bgColor: {
            type: String.value: ' ',},bgImage: {
            type: String.value: ' ',},bgSourceId: {
            type: String.value: ' ',},width: {
            type: Number.value: 660,},height: {
            type: Number.value: 660,},enableUndo: {
            type: Boolean.value: false,}},/** * The initial data of the component */
    data: {
      history: [].isExport:false
    },

    attached() {
        const sysInfo = wx.getSystemInfoSync();
        const screenWidth = sysInfo.screenWidth;
        this.factor = screenWidth / 660;

        if (typeof this.drawArr === 'undefined') {
            this.drawArr = [];
        }
        this.ctx = wx.createCanvasContext('canvas-label'.this);
        this.draw();
    },

    /** * list of component methods */
    methods: {
        toPx(rpx) {
            return rpx * this.factor;
        },
        initBg(){
            this.data.bgColor = ' ';
            this.data.bgSourceId = ' ';
            this.data.bgImage = ' ';
        },
        initHistory(){
            this.data.history = [];
        },
        recordHistory(){
            if (!this.data.enableUndo){
                return;
            }
            this.exportJson()
            .then((imgArr) = > {
                this.data.history.push(JSON.stringify(imgArr));
            })
            .catch((e) = > {
                console.error(e);
            });
        },
        undo() {
            if (!this.data.enableUndo) {
                console.log('Backoff is not enabled, please set enableUndo="{{true}}"');
                return;
            }
            if(this.data.history.length > 1) {this.data.history.pop()
                let newConfigObj = this.data.history[this.data.history.length - 1];
                this.initByArr(JSON.parse(newConfigObj));
            }else{
                console.log('This is the first step. You cannot go back.'); }},onGraphChange(n, o) {
          console.log("onGraphChange");
            if (JSON.stringify(n) === '{}') return;
            this.drawArr.push(new dragGraph(Object.assign({
                x: 30.y: 30,
            }, n), this.ctx, this.factor));
            this.draw();
            // Record the history of parameter changes
            this.recordHistory();
        },
        initByArr(newArr) {
          console.log("initByArr");
            this.drawArr = []; // Reset the painting element
            this.initBg(); // Reset the painting background
            // Loop into drawArr
            newArr.forEach((item, index) = > {
                switch (item.type) {
                    case 'bgColor':
                        this.data.bgImage = ' ';
                        this.data.bgSourceId = ' ';
                        this.data.bgColor = item.color;
                        break;
                    case 'bgImage':
                        this.data.bgColor = ' ';
                        this.data.bgImage = item.url;
                        if (item.sourceId) {
                            this.data.bgSourceId = item.sourceId;
                        }
                        break;
                    case 'image':
                    case 'text':
                        if (index === newArr.length - 1) {
                            item.selected = true;
                        } else {
                            item.selected = false;
                        }
                        this.drawArr.push(new dragGraph(item, this.ctx, this.factor));
                        break; }});this.draw();
        },
        draw() {
            if(! isExport &&this.data.bgImage ! = =' ') {
              this.ctx.drawImage(this.data.bgImage, 0.0.360.360);

              this.drawArr.forEach((item) = > {
                item.paint();
              });
            }
            if (this.data.bgColor ! = =' ') {
                this.ctx.save();
                this.ctx.setFillStyle(this.data.bgColor);
                this.ctx.fillRect(0.0.this.toPx(this.data.width), this.toPx(this.data.height));
                this.ctx.restore();
            }

          if (isExport) {
              this.ctx.drawImage(this.data.bgImage, 0.0.360.300);

              this.drawArr.forEach((item) = > {
                item.paint();
              });
              isExport = false;
            }

            return new Promise((resolve) = > {
                this.ctx.draw(false.() = > {
                    resolve();
                });
            });
        },
        start(e) {
          console.log("start");
              isMove = false; // Reset the move flag
              const {x, y} = e.touches[0];
              this.tempGraphArr = [];
              let lastDelIndex = null; // Record the last index to drop
              this.drawArr && this.drawArr.forEach((item, index) = > {
                  const action = item.isInGraph(x, y);
                  if (action) {
                      item.action = action;
                      this.tempGraphArr.push(item);
                      // Save the coordinates when clicked
                      this.currentTouch = {x, y};
                      if (action === 'del') {
                          lastDelIndex = index;// Marks the element to be deleted}}else {
                      item.action = false;
                      item.selected = false; }});// Save the information about the element when clicked
              console.log(this.tempGraphArr)
              if (this.tempGraphArr.length > 0) {
                  for (let i = 0; i < this.tempGraphArr.length; i++) {
                      let lastIndex = this.tempGraphArr.length - 1;
                      // Operate on the last element
                      if (i === lastIndex) {
                          // Unselected elements are not deleted or scaled
                          if(lastDelIndex ! = =null && this.tempGraphArr[i].selected) {
                              if (this.drawArr[lastDelIndex].action === 'del') {
                                  this.drawArr.splice(lastDelIndex, 1);
                                this.ctx.clearRect(0.0.this.toPx(this.data.width - 400), this.toPx(this.data.height - 400)); }}else {
                              this.tempGraphArr[lastIndex].selected = true;
                              this.currentGraph = Object.assign({}, this.tempGraphArr[lastIndex]); }}else {
                          // Not the last element, does not need to be selected, and does not record the state
                          this.tempGraphArr[i].action = false;
                          this.tempGraphArr[i].selected = false; }}}this.draw();
          },
          move(e) {
              const {x, y} = e.touches[0];
              if (this.tempGraphArr && this.tempGraphArr.length > 0) {
                  isMove = true; // If there is a selected element and there is a move, set the move flag
                  const currentGraph = this.tempGraphArr[this.tempGraphArr.length - 1];
                  if (currentGraph.action === 'move') {
                      currentGraph.centerX = this.currentGraph.centerX + (x - this.currentTouch.x);
                      currentGraph.centerY = this.currentGraph.centerY + (y - this.currentTouch.y);
                      // Use the center coordinates to calculate the displacement, not x,y coordinates, because it is affected by rotation.
                      if(currentGraph.type ! = ='text') {
                          currentGraph.x = currentGraph.centerX - this.currentGraph.w / 2;
                          currentGraph.y = currentGraph.centerY - this.currentGraph.h / 2; }}else if (currentGraph.action === 'transform') {
                      currentGraph.transform(this.currentTouch.x, this.currentTouch.y, x, y, this.currentGraph);
                  }
                  // Update 4 coordinate points (relative to canvas coordinate system)
                  currentGraph._rotateSquare();

                  this.draw(); }},end(e) {
          console.log("start");
            this.tempGraphArr = [];
            if(isMove){
                isMove = false; // Reset the move flag
                // Record the history at the end of a user operation
                this.recordHistory(); }},export(flag) {
          console.log(flag);
            return new Promise((resolve, reject) = > {
              this.drawArr = this.drawArr.map((item) = > {
                item.selected = false;
                return item;
              });
              this.draw().then(() = > {
                wx.canvasToTempFilePath({
                  canvasId: 'canvas-label'.width: 660.height: 660.destWidth: 1320.destHeight: 1320.success: (res) = > {
                    resolve(res.tempFilePath);
                  },
                  fail: (e) = >{ reject(e); }},this); }); })},exportJson() {
            return new Promise((resolve, reject) = > {
                let exportArr = this.drawArr.map((item) = > {
                    item.selected = false;
                    switch (item.type) {
                        case 'image':
                            return {
                                type: 'image'.url: item.fileUrl,
                                y: item.y,
                                x: item.x,
                                w: item.w,
                                h: item.h,
                                rotate: item.rotate,
                                sourceId: item.sourceId,
                            };
                            break;
                        case 'text':
                            return {
                                type: 'text'.text: item.text,
                                color: item.color,
                                fontSize: item.fontSize,
                                y: item.y,
                                x: item.x,
                                w: item.w,
                                h: item.h,
                                rotate: item.rotate,
                            };
                            break; }});if (this.data.bgImage) {
                    let tmp_img_config = {
                        type: 'bgImage'.url: this.data.bgImage,
                    };
                    if (this.data.bgSourceId) {
                        tmp_img_config['sourceId'] = this.data.bgSourceId;
                    }
                    exportArr.unshift(tmp_img_config);
                } else if (this.data.bgColor) {
                    exportArr.unshift({
                        type: 'bgColor'.color: this.data.bgColor }); } resolve(exportArr); })},changColor(color) {
            const selected = this.drawArr.filter((item) = > item.selected);
            if (selected.length > 0) {
                selected[0].color = color;
            }
            this.draw();
            // Record the history when changing the text color
            this.recordHistory();
        },
        changeBgColor(color) {
            this.data.bgImage = ' ';
            this.data.bgColor = color;
            this.draw();
            // Record history when changing background color
            this.recordHistory();
        },
        changeBgImage(newBgImg) {
            this.data.bgColor = ' ';
            if (typeof newBgImg == 'string') {
                this.data.bgSourceId = ' ';
                this.data.bgImage = newBgImg;
            } else {
                this.data.bgSourceId = newBgImg.sourceId;
                this.data.bgImage = newBgImg.url;
            }
            this.draw();
            // Record the history when changing the background image
            this.recordHistory();
        },
        clearCanvas() {
            this.ctx.clearRect(0.0.this.toPx(this.data.width), this.toPx(this.data.height));
            this.ctx.draw();
            this.drawArr = [];
            this.initBg(); // Reset the painting background
            this.initHistory(); // Clear the history}}});Copy the code

4. Save the picture locally

Preview images and is saved to the local call a picture amplification, one is saved directly to the local here I use the same method, preview image and save the call the same export canvas method, but at the time of save pictures in save the file to add a callback function, but also can be written as a method, Judging by different flags, I’m a little lazy here


  /** * previews */
  onExport() {
    CanvasDrag.export("1")
      .then((filePath) = > {
        console.log(filePath);
        wx.previewImage({
          urls: [filePath]
        })
      })
      .catch((e) = > {
        console.error(e); })},/** * save the image */
  saveImg() {
    let that = this;
    CanvasDrag.export("1")
      .then((filePath) = > {
        console.log(filePath);
        wx.saveImageToPhotosAlbum({
          filePath: filePath,
          success(res) { 
            wx.showToast({
              title: 'Saved successfully'.icon: 'none'
            })
            that.onChangeBgImage(null, that.data.canvasBg);
            //that.onChangeBgImage(null, that.data.canvasBg);
          }
        })
      })
      .catch((e) = > {
        console.error(e); })},Copy the code

Actually can also optimize the, add the function of the upload pictures, this is not a must use the avatar, modified and can add head replacement to friends or family, because I don’t want to bother just made for the function of the image to upload The overall function is such, WeChat search can modify the avatar face popular assistant 】 【 oh, Here is the first version for you to have a try. If you like it, please write a try. Happy New Year and the Year of the Tiger