Generating posters is a frequent requirement in applets development, and HERE I summarize the common steps:
- There are mainly text and background drawing, user profile picture and small program TWO-DIMENSIONAL code
- Note: The implementation of this case is based on mpVue framework, if the use of small program own framework or UNIAPP framework need to make a small amount of adjustment
Resources to prepare
Small program TWO-DIMENSIONAL code
Before generating the poster, you first need to obtain the small program QR code, which is usually generated on the server side. In this case, we will use cloud development instead. The advantage of cloud development is FAAS = Functions as a Service, which can bypass tedious back-end construction and allow development to focus on business logic (for the front end, it is a step towards all-dry engineer).
Citing a picture of Dashuai teacher, the following traditional deployment can be omitted under the cloud development mode
Developers.weixin.qq.com/miniprogram…
- Interface A can accept pages/index/index? Param =128 bytes (100 thousand bytes)? userid=xxx&from=123213
- Interface C, officially not recommended use, square is not recommended.
- Interface B must be the page where the published applet exists (otherwise an error will be reported). For example, do not add/before the root path of Pages /index/index, and do not carry parameters (please put the parameters in the scene field). If this field is not filled in, the main page will be skipped by default
The cloud function code is as follows: getMpCode/index.js
// Cloud function entry file
const cloud = require('wx-server-sdk')
cloud.init()
// Cloud function entry function
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
try {
// wechat official interface, generate small program code (interface A is used here for demonstration, interface B is recommended for the actual project)
const result = await cloud.openapi.wxacode.get({
"path": 'page/index/index'."width": 430."isHyaline":true
})
return result
} catch (err) {
return err
}
}
Copy the code
Call the cloud function to get the QR code:
// The promise wrapper is mainly used to avoid callback hell when used later.
getQrcode(){
return new Promise((resolve,reject) = >{
wx.cloud.callFunction({
name:"getMpCode".// The data to be retrieved is of type buffer
success:(res) = >{
const src="data:image/jpg; base64,"+ wx.arrayBufferToBase64(res.result.buffer);
resolve(src)
},
fail:(err) = >{ reject(err) } }); })},Copy the code
The avatars
Obtaining a user profile picture requires user authorization. My approach is to save the obtained user information locally. If there is no local user profile picture information when drawing posters, the popup box will guide users to authorize.
// Check whether the local user profile picture exists
checkUserInfo(){
const userInfoProfile = wx.getStorageSync("userProfile");
if(! userInfoProfile){return false
}
return true
},
// Check if there is a profile picture before drawing a poster
drawPoster(){
const self=this;
if(! self.checkUserInfo()){// If the user information does not exist, a dialog box is displayed to guide the user to obtain the information
return wx.showModal({
title: 'tip'.content: 'Please first authorize access to profile picture information'.async success (res) {
if (res.confirm) {
// Obtain user information after user authorization
await self.getUserprofile()
self.drawPoster();
} else if (res.cancel) {
}
}
})
};
/ /...
}
// Get user information
getUserprofile(){
return new Promise((resolve,reject) = >{
// This interface needs to be triggered by a button and cannot be used directly in the lifecycle
wx.getUserProfile({
desc:'Get your nickname, avatar'.success:res= >{
// Save the file to the local directory
wx.setStorage({
key: "userProfile".data: res.userInfo
});
resolve()
},
fail: (res) = >{
console.log(`fail`,res)
reject()
}
})
})
},
// Obtain the user profile picture from the local directory
getHeadImage(){
return new Promise((resolve,reject) = >{
// Obtain it from storage
const userProfile=wx.getStorageSync(`userProfile`);
if(! userProfile){return reject()
}
let {avatarUrl=""}=userProfile;
resolve(avatarUrl)
})
},
Copy the code
background
Here, the image is stored in the cloud storage in advance, and then call wx.getImageInfo to download the network image to the local for subsequent mapping
getBgImg(){
const self=this
return new Promise((resolve,reject) = >{
// Get the image in the cloud storage
self.getImgInfo('cloud://cloud1-8gp38tt58f1cad0e.636c-cloud1-8gp38tt58f1cad0e-1306912889/mdata/img/weiwuxian2.jpg').then(res= >{
console.log(`cloudImg`,res)
//{errMsg: "getImageInfo:ok", width: 794, height: 794, type: "jpeg", orientation: "up",path: "http://tmp/wsz3Rlfyo5yj279d60b79b9c747d19a192f79e7e9cb8.jpg",type:"jpeg",width:794}
if(res && res.path){
resolve(res.path)
}else{
reject()
}
})
})
},
getImgInfo(src){
return new Promise((resolve, reject) = > {
wx.getImageInfo({
src: src,
success: resolve,
fail: reject
})
})
},
Copy the code
Canvas style
Since the picture saved with a doubled canvas will be very fuzzy when obtaining temporary road force to save the picture, we need to process the canvas canvas in multiple times. Pixel ratio can be used as the multiplier, which is easier to process. Pixel ratio is used here, as shown below
The structure style
<template>
<view class="sharePoster">
<view>
<view class="canvasWrap">
<canvas id="myCanvas" type="2d" style="height:450px; width:300px;"></canvas>
<img :src="poster" class="poster-img" style="height:450px; width:300px; margin-top:20rpx;">
</view>
<button type="primary" style="width:500rpx" @click="drawPoster">Draw the posters</button>
<button type="primary" @click="btnSavePoster" style="width:500rpx; margin-top:20rpx">Save the posters</button>
</view>
</view>
</template>
<style scoped lang="less">
.canvasWrap{
display:flex;
flex-direction:column;
align-items:center;
padding-bottom:20rpx;
#myCanvas{
position:absolute;
left: -1000px; //canvasHide out of view}}</style>
Copy the code
The canvas adaptation
onReady(){
this.drawPoster();
},
drawPoster(){
const query = wx.createSelectorQuery()
const self=this;
/ / /... User authorization to obtain profile picture information
// Get canvas instance and context object
query.select('#myCanvas')
.fields({ node: true.size: true })
.exec(async (res) => {
self.canvas = res[0].node
self.ctx = self.canvas.getContext('2d')
// Screen pixel ratio
const dpr = wx.getSystemInfoSync().pixelRatio
self.canvas.width = res[0].width * dpr
self.canvas.height = res[0].height * dpr
// After the canvas is enlarged, the drawing scale is enlarged accordingly, so that the drawing can be calculated according to the original size
self.ctx.scale(dpr, dpr)
/ /... Draw the background text, get the avatar and the QR code, draw the avatar and the QR code})},Copy the code
Draw the background and text
drawPoster(){
const query = wx.createSelectorQuery()
const self=this;
/ /... Judge the local information and guide the user to obtain the profile picture
query.select('#myCanvas')
.fields({ node: true.size: true })
.exec(async (res) => {
/ /... Canvas fit
/ / the background
self.ctx.fillStyle='#F5F5F5'
self.ctx.fillRect(0.0.300.450)
self.ctx.fillStyle='pink'
self.ctx.fillRect(0.0.300.300)
/ / text
self.ctx.textBaseline='top'
self.ctx.textAlign='left'
self.ctx.fillStyle='# 000'
self.ctx.fontSize=120+'px'
self.ctx.fillText("I'm writing an article in nuggets, please give me a thumbs up!".20.320)
// Draw the dividing line
self.ctx.moveTo(20.340)
self.ctx.lineTo(280.340)
self.ctx.strokeStyle = '# 333'
self.ctx.stroke()
/ /... Get the profile picture and qr code, draw the profile picture and QR code, and convert it into a picture})}Copy the code
Draw profile pictures, QR codes and background images
Get profile picture, QR code and background image
As described above, the code is as follows
drawPoster(){
const query = wx.createSelectorQuery()
const self=this;
/ /... Judge the local information and guide the user to obtain the profile picture
query.select('#myCanvas')
.fields({ node: true.size: true })
.exec(async (res) => {
/ /... Canvas fit
/ /... Background and text drawing part
wx.showLoading({
title: 'Poster in production'
});
// Get the profile picture, QR code and background image
const result=await Promise.all([self.getHeadImage(),self.getQrcode(),self.getBgImg()]);
console.log(result)
/ / [" https://thirdwx.qlogo.cn/mmopen/vi_32/Al8jy0dq1soJ... OiczAdv0PBXCt5erf1WZU90csaMsXjR3ERib9BIFJskTQ / 132 ", "data: image/JPG; base64, iVBORw0KGgoAAAANSUhEUgAAAa4A... ra2vrIXKqra2tra2th8j/Cwh3EyS5PLNGAAAAAElFTkSuQmCC", "http://tmp/wsz3Rlfyo5yj279d60b79b9c747d19a192f79e7e9cb8.jpg"]
/ /... Draw profile pictures and QR codes and convert them into pictures})}// Get the user profile picture
getHeadImage(){
return new Promise((resolve,reject) = >{
// Obtain it from storage
const userProfile=wx.getStorageSync(`userProfile`);
if(! userProfile){return reject()
}
let {avatarUrl=""}=userProfile;
resolve(avatarUrl)
})
},
// Get the code of the applet
getQrcode(){
return new Promise((resolve,reject) = >{
wx.cloud.callFunction({
name:"getMpCode".success:(res) = >{
const src="data:image/jpg; base64,"+ wx.arrayBufferToBase64(res.result.buffer);
console.log(`scr`,src)
resolve(src)
},
fail:(err) = >{ reject(err) } }); })},// Get the background image
getBgImg(){
const self=this
return new Promise((resolve,reject) = >{
// Get the image in the cloud storage
self.getImgInfo('cloud://cloud1-8gp38tt58f1cad0e.636c-cloud1-8gp38tt58f1cad0e-1306912889/mdata/img/weiwuxian2.jpg').then(res= >{
console.log(`cloudImg`,res)
//{errMsg: "getImageInfo:ok", width: 794, height: 794, type: "jpeg", orientation: "up",path: "http://tmp/wsz3Rlfyo5yj279d60b79b9c747d19a192f79e7e9cb8.jpg",type:"jpeg",width:794}
if(res && res.path){
resolve(res.path)
}else{
reject()
}
})
})
},
Copy the code
Draw profile pictures, QR codes and background images
drawPoster(){
const query = wx.createSelectorQuery()
const self=this;
/ /... Judge the local information and guide the user to obtain the profile picture
query.select('#myCanvas')
.fields({ node: true.size: true })
.exec(async (res) => {
/ /... Canvas fit
/ /... Background and text drawing part
wx.showLoading({
title: 'Poster in production'
});
// Get the profile picture, QR code and background image
const result=await Promise.all([self.getHeadImage(),self.getQrcode(),self.getBgImg()]);
/ /... Draw profile picture and QR code and background picture
await Promise.all([self.drawHeadImg(result[0])], self.drawQRcode2(result[1]),self.drawBgImg(result[2])) wx.hideLoading(); })}// Draw the avatar
drawHeadImg(img){
const self=this
return new Promise((resolve,reject) = >{
Wx. getImageInfo = wx.getImageInfo = wx.getImageInfo
self.getImgInfo(img).then(res= >{
let image=self.canvas.createImage();
image.src=res.path;
image.onload=() = >{
self.ctx.save()
self.ctx.fillStyle='#fff'
// To draw a round avatar, first make a round clipping area and then draw a rectangular avatar. The final effect is a round avatar
self.ctx.beginPath()
self.ctx.arc(70.400.50.0.2 * Math.PI)
self.ctx.clip()
self.ctx.drawImage(image, 20.350.100.100);
self.ctx.restore()
resolve()
}
})
})
},
getImgInfo(src){
return new Promise((resolve, reject) = > {
wx.getImageInfo({
src: src,
success: resolve,
fail: reject
})
})
},
// Draw a qr code image
drawQRcode2(bufferData){
const self=this;
return new Promise((resolve,reject) = >{
let image=self.canvas.createImage();
// Load buffer data
image.src=bufferData;
image.onload=() = >{
self.ctx.drawImage(image, 160.350.100.100);
resolve()
}
})
},
// Draw the background image
drawBgImg(src){
const self=this;
return new Promise((resolve,reject) = >{
let image=self.canvas.createImage();
image.src=src;
image.onload=() = >{
self.ctx.drawImage(image, 0.0.300.300);
resolve()
}
})
},
Copy the code
Convert canvas image to image
drawPoster(){
const query = wx.createSelectorQuery()
const self=this;
/ /... Judge the local information and guide the user to obtain the profile picture
query.select('#myCanvas')
.fields({ node: true.size: true })
.exec(async (res) => {
/ /... Canvas fit
/ /... Background and text drawing part
wx.showLoading({
title: 'Poster in production'
});
// Get the profile picture, QR code and background image
const result=await Promise.all([self.getHeadImage(),self.getQrcode(),self.getBgImg()]);
/ /... Draw profile picture and QR code and background picture
await Promise.all([self.drawHeadImg(result[0])], self.drawQRcode2(result[1]),self.drawBgImg(result[2]))
wx.hideLoading();
// Convert the canvas into a temporary path and use IMGE to load the generated image to render on the page
self.canvas2Img()
})
}
canvas2Img(){
const self=this
setTimeout(() = > {
// Save the canvas as a temporary path for display
wx.canvasToTempFilePath({
// The entire canvas node needs to be passed in because it is 2D
canvas:self.canvas,
success: (res) = > {
// Save the path to display on the page through image
self.poster = res.tempFilePath;
},
fail:(res) = >{
console.log(`fail`,res)
}
},self)
}, 200)}Copy the code
Trigger button to save poster locally
btnSavePoster(){
const self=this;
// Save the image from the local path to the album
wx.saveImageToPhotosAlbum({
filePath: self.poster,
success: (result) = > {
wx.showToast({
title: 'Poster saved, go share it with your friends. '.icon: 'none'})},// Saving an image to an album requires user authorization. If saving fails, pull up the authorization dialog box
fail:(err) = >{
console.log(err)
if(err.errMsg === "saveImageToPhotosAlbum:fail:auth denied" || err.errMsg === "saveImageToPhotosAlbum:fail auth deny" || err.errMsg === "saveImageToPhotosAlbum:fail authorize no response") {
wx.showModal({
title: 'tip'.content: 'Need your authorization to save album'.showCancel: false.success: modalSuccess= > {
wx.openSetting({
success(settingdata) {
if (settingdata.authSetting['scope.writePhotosAlbum']) {
console.log('Obtain permission successfully, give again click the picture to save to the album prompt. ')}else {
console.log('Permission failed to be obtained. Warning is given that it cannot be used properly without permission')}}})}})}})}})},Copy the code