页面布局
<view class="mask" v-show="isShow"> <view class='board'> <view class="heart"> <span class="sure" @click="isShow=false">×</span> </view> <canvas @longpress="saveHomepage" :style="'width:'+canvasWidth+'px;height:'+ canvasHeight+'px;'" canvas-id="myCanvas"></canvas> </view> </view> <view class="foot-icon-card" @click="addCanvas(activity)"> <image src="../../static/icons/haibao.svg"> </image> <text>生成海报</text> </view>
data属性定义
canvasWidth: 300, canvasHeight: 500, isShow: false, img_pop: '', poster: '' imageList: [ "https://s1.ax1x.com/2023/06/02/p9zHfp9.jpg", "https://s1.ax1x.com/2023/06/02/p9zHT0K.jpg", "https://s1.ax1x.com/2023/06/02/p9zHhlR.jpg", "https://s1.ax1x.com/2023/06/02/p9zHRfJ.jpg", "https://s1.ax1x.com/2023/06/02/p9zH2Y4.jpg", "https://s1.ax1x.com/2023/06/02/p9zHom6.jpg", "https://s1.ax1x.com/2023/06/02/p9zH5Ox.jpg", "https://s1.ax1x.com/2023/06/02/p9zH461.jpg", ]
methods实现
//文字自动换行显示,超出指定范围用...代替显示 textSplit(ctx, text, lineWidth, lines) { const charWidths = Array.from(text, char => ctx.measureText(char).width); let textArr = []; let c_text = ''; let lineCount = 0; for (let i = 0; i < text.length; i++) { const charWidth = charWidths[i]; if (ctx.measureText(c_text).width + charWidth < lineWidth) { c_text += text[i]; } else { lineCount++; if (lineCount === lines) { const lastLineWidth = ctx.measureText(c_text).width; c_text = c_text.slice(0, -1); while (ctx.measureText(c_text + '...').width > lineWidth) { c_text = c_text.slice(0, -1); } c_text += '...'; textArr.push(c_text); return textArr; } else { textArr.push(c_text); c_text = text[i]; } } } // 当长度小于指定的 `lineWidth` 时,直接返回传入的文本内容 if (textArr.length === 0) { return [text]; } // 添加最后一行文本内容 textArr.push(c_text); // 如果超过了指定行数,则修改最后一行内容,并返回修改后的文本数组。 if (textArr.length > lines) { const lastLineWidth = ctx.measureText(c_text).width; c_text = textArr.pop(); while (ctx.measureText(c_text + '...').width > lineWidth) { c_text = c_text.slice(0, -1); } c_text += '...'; textArr.push(c_text); } return textArr; }
//图片网络路径转换为临时路径 getImgUrl(img) { return new Promise((result, rej) => { wx.getImageInfo({ src: img, success: (res) => { console.log(res, "获取"); return result(res.path) }, fail: (error) => { rej(error) } }) }) },
async addCanvas(item) {
this.isShow = true
this.foot = 'close'
uni.showLoading({
title: '正在生成海报'
})
let ctx = wx.createCanvasContext('myCanvas')
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight)
// 封面图
if (item.avatar) {
ctx.drawImage(await this.getImgUrl(item.avatar), 0, 0, this.canvasWidth, 200)
} else {
//随机背景图
ctx.drawImage(await this.getImgUrl(this.imageList[Math.floor(Math.random() * 8)]), 0, 0, this
.canvasWidth, 200)
}
//标题
ctx.font = `normal 20px `
ctx.fillStyle = '#000'
let nameText = this.textSplit(ctx, item.name, this.canvasWidth - 20, 1)
nameText.forEach(r => {
ctx.fillText(r, 15, 220)
})
//活动内容
let lineHeight = 25; //文字行高
let text = this.textSplit(ctx, item.description, this.canvasWidth + this.canvasWidth / 3, 3)
text.forEach((r, index) => {
ctx.font = `normal 14px `
ctx.fillStyle = '#636363'
ctx.fillText(r, 15, 250 + index * lineHeight)
})
//地点
ctx.font = `normal 14px `
ctx.fillStyle = '#000'
ctx.fillText('活动地点:', 15, 350)
let textAdress = this.textSplit(ctx, item.address, this.canvasWidth - 100, 2)
textAdress.forEach((r, index) => {
ctx.font = `normal 14px `
ctx.fillStyle = '#636363'
ctx.fillText(r, 15, 370 + index * lineHeight)
})
//时间
ctx.font = `normal 14px `
ctx.fillStyle = '#000'
ctx.fillText('时间:', 15, 420)
ctx.font = `normal 14px `
ctx.fillStyle = '#000'
ctx.fillText(item.start_date + " " + item.start_time, 15, 440)
// 二维码
ctx.drawImage(this.img_pop, this.canvasWidth / 2 + this.canvasWidth / 4, 340, 70, 70)
// 底部文字
ctx.font = 'normal 12px '
ctx.fillStyle = '#9C9C9C'
ctx.fillText('长按识别或保存图片', this.canvasWidth / 3, 470)
//开始绘画
await ctx.draw(true, res => {
setTimeout(() => {
uni.canvasToTempFilePath({
canvasId: 'myCanvas',
success: res => {
this.poster = res.tempFilePath
uni.hideLoading()
},
fail: err => {
uni.hideLoading()
}
}, this)
}, 1000)
})
},
// 判断是否有写入相册的权限 saveHomepage() { let that = this; //判断当前小程序是否有保存相册的权限 wx.getSetting({ success(res) { if (!res.authSetting['scope.writePhotosAlbum']) { wx.authorize({ scope: 'scope.writePhotosAlbum', success(res) { that.startSaveImage(); }, fail() { //这里是用户拒绝授权后的回调 wx.showModal({ content: '请允许相册权限,拒绝将无法正常使用小程序', showCancel: false, success() { wx.openSetting({ success(settingdata) { if (settingdata.authSetting[ "scope.writePhotosAlbum" ]) {} else { console.log("获取权限失败") } } }) } }) } }) } else { that.startSaveImage(); } } }) },
//保存本地 startSaveImage() { uni.saveImageToPhotosAlbum({ filePath: this.poster, success: (res) => { uni.showToast({ title: '保存成功', icon: 'none', duration: 4000 }) this.$refs.popup.close() } }) },
css样式
.mask { position: fixed; top: 0; left: 0; z-index: 998; width: 100vw; height: 100%; background-color: #000000; overflow: hidden; } .board { animation: myframe 0.3s; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 999; } .sure { text-align: center; display: block; width: 46rpx; margin: 0 auto; font-size: 32rpx; color: #fff; height: 46rpx; border-radius: 50%; line-height: 44rpx; border: 1px solid #ffffff; font-family: '楷体'; } .heart { position: relative; bottom: -540px; }
效果图