前言
因产品需求,需要在小程序中生成一张分享海报用于产品推广。特此记录一波产出过程~
这次开发使用的是 uni-app 来产出小程序
Part.1 大致思路
按照设计图将所需元素全部画入 Canvas 画布,再利用 wx.canvasToTempFilePath(Object object, Object this) API 将 Canvas 生成一张指定大小的图片,保存分享即可~
此API具体用法和注意事项可去 https://developers.weixin.qq.com/miniprogram/dev/api/canvas/wx.canvasToTempFilePath.html 查看
Part.2 遇到的问题
1. 由于此次 Canvas 中的内容不是固定的,可能会有很多内容。所以第一个遇到的问题就是: Canvas 中的文字换行
2. 文字换行成功后又遇到一个新问题:需要换行的文字中 不能包含空格字符,否则内容排版会错乱
3. 由于可能出现内容很多,不可控的情况,所以 Canvas 的高度也不好确定 : Canvas 高度不确定
4. 使用网络图片画入 Canvas 中
5. 产出的 Canvas 图片模糊不清
Part.3 解决问题
问题一: 解决 Canvas 文字换行的方法
// canvas 文本换行绘制 drawtext(ctx, t, x, y, w, num) { //参数说明 //ctx:canvas的 2d 对象,t:绘制的文字,x,y:文字坐标,w:文字最大宽度 let chr = t.split(""); let temp = ""; let row = []; for (let a = 0; a<chr.length;a++) { if(ctx.measureText(temp).width < w && ctx.measureText(temp+(chr[a])).width <= w){ temp += chr[a]; }else{ row.push(temp); temp = chr[a]; } }; row.push(temp); let rowLength = row.length; if (num == 1) { this.titleLineH = (rowLength + 1) * uni.upx2px(48); } else { this.contentLineH = (rowLength + 1) * uni.upx2px(48); }; for (let b = 0; b < rowLength; b++){ ctx.fillText(row[b], x, y + (b + 1) * uni.upx2px(48));//每行字体y坐标间隔24 } },
问题二:利用 xxx.replace(/\s/g, ""),去掉内容中的空格字符
问题三: Canvas 高度设置为动态, 例:height: 5000px
问题四: 使用网络图片画入时,需先使用 uni.getImageInfo 这个API加载图片完成拿到临时地址后再绘入 Canvas ,否则很容易造成 Canvas 出现空白
问题五: 产出的 Canvas 图片模糊,只需将输出值扩大原来的两倍即可 ,如:200px 输出就为 400px
Part.4 代码展示
HTML
1 <template> 2 <view class="layout-bg"> 3 4 <view class="scroll-Y"> 5 6 <canvas canvas-id=‘share‘ 7 class="canvas-style" 8 :style="{height: canvasHeight + ‘px‘}"></canvas> 9 10 <view class="img-box"> 11 <image class="img-placard" 12 :style="{height: canvasHeight + ‘px‘}" 13 @load="placardLoad()" 14 :src="src"></image> 15 </view> 16 17 <view :style="{height: canvasHeight + 100 + ‘px‘}"></view> 18 </view> 19 20 21 <view class="footer-btn"> 22 <view class="row footer-left-box" hover-class="hover-active" @click="downloadWay()"> 23 <image class="btn-img preserve-ico" 24 src="../../static/image/download-ico.svg"></image> 25 <button class="btn-text preserve-btn" 26 :class="text == ‘长按上方图片即可保存‘? ‘text-color‘ : ‘‘">{{text}}</button> 27 </view> 28 29 <view class="btn-share-box row" hover-class="hover-active"> 30 <image class="btn-img share-ico" 31 src="../../static/image/share-ico.svg"></image> 32 <button class="btn-text share-btn" open-type="share">分享</button> 33 </view> 34 </view> 35 </view> 36 </template>
JS
<script> export default { data() { return { src: ‘‘, obj: {}, content: ‘‘, simpleMsgTitle: ‘标题‘, simpleMsgTime: ‘2019-11-23 17:20‘, textTitle: ‘中振区块链技术全球战略合作媒体‘, textDesc: ‘扫码查询获取更多资讯‘, text: ‘‘, canvasHeight: 5000, // canvas 默认高度 canvasStatus: false, query: {} } }, onLoad(event) { // #ifdef MP-WEIXIN uni.showLoading({ title: ‘加载中‘ }); this.text = ‘下载分享海报‘ // #endif try { this.obj = JSON.parse(decodeURIComponent(event.query)); } catch (error) { this.obj = JSON.parse(event.query); }; uni.setNavigationBarTitle({ title: "快讯" }); this.query = this.obj; this.simpleMsgTitle = this.obj.title; this.simpleMsgTime = this.obj.datetime; this.content = this.obj.content; }, onReady() { // 开始绘制海报 this.setCanvas() }, onShareAppMessage(res) { return { title: this.query.title, path: ‘/pages/simpleMsg/share?query=‘ + encodeURIComponent(JSON.stringify(this.query)) } }, methods: { // 开始绘制海报 setCanvas() { // 判断图片是否加载完成 let imgArr = [‘https://qdksm.zhongzhenbc.com/banner.png‘, ‘https://qdksm.zhongzhenbc.com/qdCode.png‘]; uni.getImageInfo({ src: imgArr[0], success: (img1)=> { if (img1.path !== ‘‘) { uni.getImageInfo({ src: imgArr[1], success: (img2)=> { if (img2.path !== ‘‘) { const ctx = wx.createCanvasContext(‘share‘, this); ctx.fillStyle = "#2C3339"; ctx.fillRect(0, 0, uni.upx2px(672), this.canvasHeight); ctx.drawImage(img1.path, 0, 0, uni.upx2px(672), uni.upx2px(202)); // h: 202 ctx.setFontSize(uni.upx2px(24)); ctx.setFillStyle(‘rgba(79,234,207,1)‘); ctx.fillText(‘快讯‘, uni.upx2px(30), uni.upx2px(249)); ctx.setFontSize(uni.upx2px(36)); ctx.setFillStyle(‘rgba(255,255,255,1)‘); ctx.fillText(‘奇点科商美‘, uni.upx2px(110), uni.upx2px(90)); ctx.setFontSize(uni.upx2px(24)); ctx.fillText(‘科技商业美学引领者‘, uni.upx2px(110), uni.upx2px(120)); ctx.fillText(‘一站式前沿科技精英社区‘, uni.upx2px(110), uni.upx2px(150)); ctx.setFontSize(uni.upx2px(32)); this.drawtext(ctx, this.obj.title, uni.upx2px(30), uni.upx2px(259), uni.upx2px(612), 1) ctx.setFontSize(uni.upx2px(24)); ctx.setFillStyle(‘rgba(105,117,130,1)‘); ctx.fillText(this.obj.datetime, uni.upx2px(30), uni.upx2px(269) + this.titleLineH); ctx.setFontSize(uni.upx2px(28)); ctx.setFillStyle(‘rgba(244,244,244,1)‘); this.drawtext(ctx, " "+this.obj.content.replace(/\s/g, ""), uni.upx2px(30), uni.upx2px(279) + this.titleLineH, uni.upx2px(612), 2); ctx.moveTo (0, this.titleLineH + this.contentLineH + uni.upx2px(289)); //设置起点状态 ctx.lineTo (uni.upx2px(672), this.titleLineH + this.contentLineH + uni.upx2px(289)); //设置末端状态 ctx.drawImage(img2.path, uni.upx2px(30), this.titleLineH + this.contentLineH + uni.upx2px(315), uni.upx2px(160), uni.upx2px(160)); ctx.setFontSize(uni.upx2px(24)); ctx.setFillStyle(‘rgba(79,234,207,1)‘); ctx.fillText(‘中振区块链技术全球战略合作媒体‘, uni.upx2px(272), this.titleLineH + this.contentLineH + uni.upx2px(362)); ctx.setFillStyle(‘rgba(255,255,255,1)‘); ctx.fillText(‘扫码查询获取更多资讯‘, uni.upx2px(392), this.titleLineH + this.contentLineH + uni.upx2px(412)); this.canvasHeight = this.titleLineH + this.contentLineH + uni.upx2px(511); // 根据内容高度重新为 canvas 赋值高度 ctx.lineWidth = 1; //设置线宽状态 ctx.strokeStyle = "#697582" ; //设置线的颜色状态 ctx.stroke(); ctx.draw(true, ()=> { uni.canvasToTempFilePath({ x: 0, y: 0, width: uni.upx2px(672), height: this.canvasHeight, destWidth: uni.upx2px(1244), destHeight: this.canvasHeight * 2, canvasId: ‘share‘, success: (res)=> { this.src = res.tempFilePath; this.canvasStatus = true },fail(err) { console.log(err) } },this) },200); } } }) } } }) }, // canvas 文本换行绘制 drawtext(ctx, t, x, y, w, num) { //参数说明 //ctx:canvas的 2d 对象,t:绘制的文字,x,y:文字坐标,w:文字最大宽度 let chr = t.split(""); let temp = ""; let row = []; for (let a = 0; a<chr.length;a++) { if(ctx.measureText(temp).width < w && ctx.measureText(temp+(chr[a])).width <= w){ temp += chr[a]; }else{ row.push(temp); temp = chr[a]; } }; row.push(temp); let rowLength = row.length; if (num == 1) { this.titleLineH = (rowLength + 1) * uni.upx2px(48); } else { this.contentLineH = (rowLength + 1) * uni.upx2px(48); }; for (let b = 0; b < rowLength; b++){ ctx.fillText(row[b], x, y + (b + 1) * uni.upx2px(48));//每行字体y坐标间隔24 } }, // 海报加载完成事件 placardLoad() { uni.hideLoading(); }, // 保存海报 downloadWay() { let that = this; // #ifdef H5 that.text = ‘长按上方图片即可保存‘; // #endif // #ifdef MP-WEIXIN that.text = ‘下载分享海报‘; uni.authorize({ scope: ‘scope.writePhotosAlbum‘, success() { uni.saveImageToPhotosAlbum({ filePath: that.src, success: function() { uni.showModal({ title: ‘保存成功‘, content: ‘已保存到相册,快去看看吧‘, showCancel: false }) } }) },fail:function() { uni.showModal({ title: ‘提示‘, content: ‘您点击了拒绝授权,将无法正常保存图片,点击确定可重新获取授权‘, success: function (res) { if (res.confirm) { uni.openSetting({ success: (res) => { if (res.authSetting["scope.writePhotosAlbum"]) {////如果用户重新同意了授权登录 uni.saveImageToPhotosAlbum({ filePath: that.src, success: function() { uni.showModal({ title: ‘保存成功‘, content: ‘已保存到相册,快去看看吧‘, showCancel: false }) } }) } }, fail: function (res) {} }) } } }) } }) // #endif } } } </script>
CSS
<style scoped> button::after{ border: none } .scroll-Y { display: flex; justify-content: center; align-items: center; position: relative; } .canvas-style, .img-box { position: absolute; top: 40upx; } .canvas-style { width: 672upx; top: -9999rpx } .position-absolute { position: absolute; } .footer-btn, .row { flex-direction: row; } .row { z-index: 9999; } .layout-bg { background-color: #20252B; } .footer-btn { width: 100%; height: 100upx; position: fixed; bottom: 0; left: 0; right: 0; background-color: #2C3339; z-index: 9999; } .footer-left-box, .btn-share-box{ width: 50%; height: 100upx; align-items: center; justify-content: center; border-radius: 10upx; position: relative; } .btn-img { width: 40upx; height: 40upx; position: absolute; top: 30upx } .preserve-ico { left: 89upx; } .share-ico { left: 138upx; } .btn-text { font-size: 24upx; font-weight:400; color:rgba(105,117,130,1); background-color: transparent; } .download-img { margin-left: 88upx; } .btn-download-box2 { width: 100%; justify-content: center; } .download-img2 { margin-left: 0upx; } .img-box,.img-placard { z-index: 998; } .share-btn,.preserve-btn { width: 100%; height: 100rpx; font-size: 24rpx; font-weight: 400; color: rgba(105,117,130,1); background-color: transparent; padding-left: 50upx; padding-right: 0; line-height: 104upx; } .hover-active { opacity: 0.9; background: #20252B; } .text-color { color:rgba(79,234,207,1); } .img-placard { width: 672upx; border-radius: 10upx; box-shadow: 0px 1px 3px rgba(34, 25, 25, 0.2); } button { padding-left: 0; padding-right: 60upx; } </style>
原文地址:https://www.cnblogs.com/langxiyu/p/12693280.html