In the recent business update of the company, the small program side added similar activities of sharing and creating new ones. However, considering that a large number of sharing will lead to the link being blocked by goose factory, we decided to use two-dimensional code pictures to share. So there will be a new problem from the interaction, when the generation of two-dimensional code after how to save the picture and then share out? Of course, the simplest operation is to let the user take a screenshot to share, but we can’t dump the operation on the user (this is really my real idea ~), finally decided to study the small program two-dimensional code download, so there is this share.

Two quick questions before entering the body:

1, micro channel small program set element hidden but need this element to do interaction, how to operate?

The problem is to hide a Canvas element on the page, but this element must also be available through JS. If we use something like display: None, the element disappears completely and is logically unavailable. So consider using visibility or option to set the element so that it can be both present and hidden. However, the ideal is always full, which is ok in Alipay and wechat development tools, the elements are not displayed, but in the real machine wechat small program, the canvas will still be displayed after drawing, it seems that CSS is not effective at this time.

We have to change the idea, we set the canvas element to fixed position, and then set the value of top and left to -9999, so that it can avoid appearing in the viewport, and the element exists at the same time.

2. The Canvas fillText method does not make text displayed in a folded line

When using Canvas to draw text, it may be displayed in folded lines because there are too many words, but canvas does not fold automatically. This property controls the display width of text intelligently. If the value is exceeded, the text will be directly compressed and cannot be read properly. .

There is no better way to control this problem, but to write their own methods to control, the basic idea is to convert the text into a string, and then according to the length of the text has been drawn to determine whether the length has exceeded the specified length, if it exceeds the line display, directly on the code.

@params: imgCanvas: Canvas ID title: Draw text x: start position of text x Y: start position of text y maxW: set maximum width rpx2px: CanvasText (imgCanvas, title, x, y, maxW) {let that = this var CHR = title. Split (""); var temp = ""; var row = []; ImgCanvas. SetFontSize (that.rpx2px(26)) imgCanvas. SetFillStyle ("#9a9a9a"); imgCanvas.setTextBaseline("middle"); for (var a = 0; a < chr.length; A++) {if (imgcanvas.measuretext (temp).width < maxW) {console.log(' normal range ')} else {row.push(temp); temp = ""; } temp += chr[a]; } row.push(temp); console.log(row) for (var b = 0; b < row.length; b++) { imgCanvas.fillText(row[b], x, y + (b + 1) * that.rpx2px(28)); } } rpx2px(rpx) { const windowW = wx.getSystemInfoSync().windowWidth; const px = rpx * windowW / 750; return px; }Copy the code

Small problems to be dealt with, the following into the main part of the text, mainly through two angles to say about the download picture problem: the first is directly download generated two-dimensional code pictures; The second is the TWO-DIMENSIONAL code and other elements (pictures, text) synthesis of a map for download. The basic idea is the same, the difference is whether or not to synthesize the image in the end. Of course, the basic process of wechat small program and Alipay small program is the same, this paper mainly takes wechat small program as an example, and will briefly say the similarities and differences of Alipay small program. (Note: rpx2px method in the following code is the same as above)

First, download two-dimensional code pictures directly

1. Generate two-dimensional code pictures and get the path required for downloading

To generate the qrcode, we use the appellate-qrcode tool library. You can download js files locally and use them in projects. The idea is to pass in the URL and call the little program canvasToTempFilePath in the callback to get the url:

// canvasWidth: 334, canvasHeight: 334 -- canvas.wxml --> <canvas canvas-id="sharecode" style="width: {{canvasWidth}}rpx; height: {{canvasHeight}}rpx;" / > <! -- canvas.js --> let url = 'https://www.test.com?a=1&b=2' let that = this QRcode({ width: that.rpx2px(canvasWidth), height: that.rpx2px(canvasHeight), canvasId: 'sharecode', text: url, _this: that, callback: {setTimeout(()=>{wx.canvastotempFilepath (()=>{wx.canvastotempFilepath (()=>{wx.canvastotempFilepath ( this.rpx2px(canvasWidth), height: this.rpx2px(canvasHeight), canvasId: 'sharecode', success: function(res) { console.log('[[[[[[', res) let path = res.tempFilePath that.setData({ qrCodePath: Path, // qr code download path})},})}, 100)}});Copy the code

2. The user clicks the button to save the picture

As we all know, small procedures involve privacy issues, is the need for user authorization, so before entering the formal save logic, first need to check the authorization of saving pictures to the system (key method in the code notes, related API can go to view the document ~).

SavePicAuth: function(){const that = this; // Error if(! Tha.data.qrcodepath){wx.showToast({title: 'failed to obtain qr code information, please try again later ', icon: 'none'}) return false} wx.getsetting ({success:(res)=>{if (! Res.authsetting [' scope.writepHotoSALbum ']){// Open authorization panel wx.authorize({scope:' scope.writepHOtosalbum ', success:(res)=>{ console.log('save photo:', res) this.save() }, fail: Err => {if(err.errmsg.includes ('authorize:fail')) {// Re-open the album permission wx.openSetting({success(settingData) {if (settingData.authSetting [' scope.writephotosalbum ']) {this.save()}else {// Failed to get permission, Wx.showtoast ({title: 'failed to save images ', icon:' None ',})}}})} else {this.save(); wx.showtoast ({title: 'failed to save images ', icon:' None ',})}}})} else {this.save(); }}})},Copy the code

The authorization logic passes, and then enters the saving process. The same wechat mini program also provides the related method saveImageToPhotosAlbum

Save: the function () {wx. SaveImageToPhotosAlbum ({filePath: that. Data. QrCodePath, / / the qr code to download save the above path to success: (res)=>{wx.showtoast ({title: 'save successfully ', icon: 'none',})}, fail (res)=>{wx.showtoast ({title:' save successfully ', icon: 'none',})}, fail (res)=>{wx.showtoast ({title: 'save successfully ', icon:' save successfully ',})}, fail (res)=>{wx.showtoast ({title: 'save successfully ', icon: 'none', }) } }) }Copy the code

At this point, the TWO-DIMENSIONAL code picture can be successfully downloaded, sprinkle flowers ~~~

When we were jubilant, the little brother of the product appeared and pointed to the huge two-dimensional code picture in the mobile phone and said, “Is it too big? Can we make such a picture to save?” Then opened the shared picture of a treasure……

Two, two-dimensional code and picture synthesis of a map download

The overall logic of synthesizing two-dimensional code pictures is basically the same as that of downloading two-dimensional code alone. The difference lies in that after authorization is passed, two-dimensional code is not downloaded directly, but a picture synthesized by canvas is downloaded.

Since the composition is still a canvas, we need a new carrier to carry the canvas image

<canvas canvas-id="sharecode_img" style="width: {{shareCanvasWidth}}rpx; height: {{shareCanvasHeight}}rpx;" />Copy the code

And then we add in the original save method canvas rendering logic, through wx. After canvasToTempFilePath method to get the new path, again with wx. SaveImageToPhotosAlbum to save.

// canvasWidth: 626, canvasHeight: 838 new canvas size, px unit save() {const that = this; Wx. showLoading({title: 'Loading... ', icon: 'loading'}) // Two-dimensional code and pictures // two-dimensional code information, Let promise1 = new Promise(resolve)=>{wx.getimageinfo (resolve)=>{wx.getimageinfo (resolve) : Tha.data. qrCodePath, // Support network path (domain name needs to be configured on the management platform first), local path, code package path SUCCESS: (result) => {console.log(' console.log ') ', result) resolve(result)},})}) let promise2 = new Promise((resolve)=>{wx.getimageinfo ({SRC: 'https://www.test.com/images/test.png', success: (result) => {console.log(result) resolve(result)},})}) promise. all([promise1, promise2]).then((res)=>{ console.log('all res===>', Res) // New and created Canvas carrier let imgCanvas = wx.createcanvasContext ('sharecode_img') // Entire Canvas image size let shareCanvasWidth = That. Rpx2px (that) the data) shareCanvasWidth) let shareCanvasHeight. = that rpx2px (that) the data) shareCanvasHeight) / / qr code size and position  canvasWidth = that.rpx2px(that.data.canvasWidth) let canvasHeight = that.rpx2px(that.data.canvasHeight) let qrcode_x = That. Rpx2px (146) // Let qrcode_y = that. Rpx2px (354) // Let qrcode_y = that. Rpx2px (52) let personH = that.rpx2px(52) let person_x = that.rpx2px(40) let person_y = Rpx2px (58) let personT = 'I am short' let personTSize = that.rpx2px(32) let personT_x = that.rpx2px(107) let PersonT_y = tha.rpx2px (68) // Let personM = 'let personM_x = Tha.rpx2px (107) let personM_y = tha.rpx2px (73) let personM_max = tha.rpx2px (466) // Draw the background imgCanvas.setFillStyle('white') imgCanvas.fillRect(0, 0, shareCanvasWidth, shareCanvasHeight) imgCanvas.drawImage(res[1].path, person_x, person_y, personW, ImgCanvas. DrawImage (res[0]. Path, qrcode_x, qrcode_y, canvasWidth, ImgCanvas. SetFontSize (personTSize) imgCanvas. SetFillStyle ('#000000') imgCanvas. FillText (personT, PersonT_x, personT_y) CanvasText (imgCanvas, personM, personM_x, personM_y, ImgCanvas. Draw () setTimeout(()=>{// Get the newly generated Canvas link wx.canvastotempFilepath ({x: 0, y: 0, width: 0) shareCanvasWidth, height: shareCanvasHeight, canvasId: 'sharecode_img', success: function(res) { console.log('share img mes===>', res) let path = res.tempFilePath that.setData({ shareFilePath: path, }, ()=>{ wx.hideLoading() wx.saveImageToPhotosAlbum({ filePath: that.data.shareFilePath, success: (res)=>{wx.showtoast ({title: 'save successfully ', icon: 'none',})}, fail (res)=>{wx.showtoast ({title:' save successfully ', icon: 'none',})}, fail (res)=>{wx.showtoast ({title: 'save successfully ', icon:' save successfully ',})}, fail (res)=>{wx.showtoast ({title: 'save successfully ', icon: 'none',})}})})}, fail: function (err) {the console. The log (' failure generating share figure = = = > 'to err) wx. HideLoading ()}})}, 100)})},Copy the code

After the above overall process, wechat small program can be perfect to achieve the picture synthesis download, again flower ~~~

Iii. Brief introduction of differences in relevant procedures of Alipay mini program

Micro letter small program perfect solution, the next to fix the alipay. Usually this kind of operation process is similar, copy the past to change the line. But copy after the past found that it does not work! Because some treasure small program related method and goose factory is not the same, but had to check along while API after modification.

Although there are differences in related methods, the overall process is similar. I will not describe the overall process, but simply say the differences:

1. Definition of Canvas ID:

In wechat, id is added by canvas-id=””; And alipay is added by ID =””

2, obtain two-dimensional code path method

Alipay does not have the canvasToTempFilePath method. Instead, we get it from the Canvas entity class method:

let qecodeCanvas = my.createCanvasContext('sharecode') qecodeCanvas.toTempFilePath({ x: 0, y: 0, width: canvasWidth, height: canvasHeight, canvasId: 'sharecode', success: function(res) { console.log('share code===>', res) let path = res.apFilePath that.setData({ qrCodePath: path, }) }, fail(res) { console.log('get img error===>', res.error); }})Copy the code
3. User album permission authorization problem

When Alipay saves the picture, it calls the method my.saveImage, which will decide whether to invoke the authorization window according to whether the user is authorized or not. While this may seem convenient, if a user turns off photo album authorization for a small program, he can’t directly determine the status. So we need another layer of authorization judgment.

savePicAuth: Function (){my.getsetting ({success (res)=>{function(){my.getsetting ({success (res)=>{ Because the writePhotosAlbum field does not exist in res.authSetting for the first time, this will lead to the direct opening of the Settings page, but because the Settings page is not authorized, there will not be an album open button, the logic is not working. OpenSetting ({success(settingData){if (res.authSetting['writePhotosAlbum'] === false){ Console. log('again open===>', settingData) if (settingData. authSetting['writePhotosAlbum']) { }else {// Failed to obtain permissions, give permissions can not be used properly my. ShowToast ({content: 'Authorization failed, cannot save image'})}})} else {that.save(); }}})}Copy the code
4. Method of saving pictures

The small program of Alipay saves the picture using my. SaveImage, which also needs to pass in the URL of the picture.

The above is the simple logic about the small program downloading pictures. At the same time, it is equivalent to getting familiar with the use of canvas in a way during the development process. After all, canvas is not used much in daily development. In this article to record the development problems and process, also hope to help partners in need ~

If you have any questions, please point out