文章目录
- canvas使用记录
- 先看下实际效果图
- 绘制流程及思路
- 1. 绘制头像, 通过`drawImage`来绘制
- 2.绘制文字部分
- 具体代码
分享海报图片的方式,以前再RN端采用的是截图方案, 我记得组件好像是
react-native-view-shot
现在要处理uni-app的海报图片分享, 一般也有
html2canvas
的相关插件
不过其缺点也有,
比如说遇到bug,有时候没办法修改什么的
手绘canvas虽然麻烦,但是胜在自由灵活
canvas使用记录
先看下实际效果图
绘制流程及思路
CanvasContext文档
其实使用下来发现, canvas绘制和iOS原生开发进行UI绘制有很多相似之处, 比如draw的入参 ,都需要x
,y
坐标,设置width/height等
整个canvas绘制的思路如下
0.绘制整个大矩形背景 : 通过uni.downloadFile
下载网络图片来获取tempFilePath
, 结合几个坐标和宽高参数, 就可以绘制了
1. 绘制头像, 通过drawImage
来绘制
不过这里需要注意的是, 如果要对头像图片进行裁剪,比如圆心之类的
需要用到clip
的情况下,
需要提前保存好上下文画布
// 2.顶部头像ctx.save(); // 先保存之前的画布ctx.beginPath()ctx.arc((200 + 190 / 2) * scaleNum, (60 + 190 / 2) * scaleNum, 190 / 2 * scaleNum, 0,Math.PI * 2)ctx.clip()ctx.drawImage(this.headUrl, 200 * scaleNum, 60 * scaleNum, 190 * scaleNum, 190 *scaleNum)
(200: 左边间距)
(190: 圆的直径)
(60: top的间距)
(这里需要注意的事, 圆的圆心坐标, 是相对于x/y坐标的哦 )
2.绘制文字部分
这个没有太大的问题, 不过要注意下 textAlign
的具体用法
具体代码
- html部分
<canvas :style="{width: boxDetail.width +'px', height: boxDetail.height + 'px' }"id="sharePic" canvas-id="sharePic"></canvas>
2.js部分
setCanvasSize() {const query = uni.createSelectorQuery().in(this);query.select('#content').boundingClientRect(data => {this.boxDetail.width = data.widththis.boxDetail.height = data.heightconsole.log(this.boxDetail, data)this.$nextTick(() => {setTimeout(() => {console.log('create and set canvas')this.createCanvas()// this.asyncAwaitCanvas()}, 800)})}).exec();},/*** @Description: 创建canvas画布*/async createCanvas() {this.context = uni.createCanvasContext('sharePic', this)const ctx = this.context;function drawCanvas(url, top) {if (url) {return new Promise((resolve, reject) => {let objobj = { url }uni.downloadFile({url,success: (res) => {top ? resolve({ ...res, top }) : resolve({ ...res })},fail: (err) => {reject(err)}})})} else {return new Promise((resolve, reject) => {top ? resolve({ top }) : resolve()})}}const scaleNum = this.boxDetail.width / 590// 1.背景图// - 读背景图资源let res = await drawCanvas("https://cdn.froglesson.com/static/cert/share_notes_content_bg.png");ctx.save();// - 绘制背景图路径/坐标/宽/高ctx.drawImage(res.tempFilePath, 0, 0, scaleNum * 590, scaleNum * 864)// 2.顶部头像ctx.save(); // 先保存之前的画布ctx.beginPath()ctx.arc((200 + 190 / 2) * scaleNum, (60 + 190 / 2) * scaleNum, 190 / 2 * scaleNum, 0,Math.PI * 2)ctx.clip()ctx.drawImage(this.headUrl, 200 * scaleNum, 60 * scaleNum, 190 * scaleNum, 190 *scaleNum)// 3.titlectx.restore() //恢复一下绘画板let title = this.returnName(this.shareData.user_name + ",已学习" + (this.shareData.count || 0) +"天")ctx.setFontSize(32 * scaleNum) //字体大小ctx.setFillStyle('#fff') //文字颜色ctx.setTextAlign('center') //文本左对其ctx.fillText(title, 590 / 2 * scaleNum, (280 + 30) * scaleNum, 530 *scaleNum); // marginleft:182rpx;maxWidth:374 最大宽度// 4.学习内容let content = "今天在「XXXXAPPXXXXX」" + (this.shareData.times > 30 ?`${this.shareData.times || 0}分钟` : "学习了")content = content + this.studyTitle() + this.studyCount();content = "我是中间文字部分,阿爸不不不不不不不不不,啊电话多好多好多好多好等哈,多撒MDJSLSJL"// - 字符拆分/计算行数let content_top = 0let contents = content.trim().split("")contents = contents.map((item, index) => {if (index && !(index % 20)) {item = item + '\n';}return item})// 逐行绘制标题文字contents.join("").split('\n').forEach((item, index) => {ctx.setFontSize(28 * scaleNum) //字体大小ctx.setFillStyle('#fff') //文字颜色ctx.setTextAlign('center') //文本左对其content_top = (365 + 30) * scaleNum + 48 * scaleNum * index //高度计算, 40是行高ctx.fillText(item, 590 / 2 * scaleNum, content_top, 530 *scaleNum); // marginleft:182rpx;maxWidth:148 最大宽度})// 5.时间let time = this.shareData.dayctx.setFontSize(24 * scaleNum) //字体大小ctx.setFillStyle('#fff') //文字颜色ctx.setTextAlign('center') //文本左对其ctx.fillText(time, 590 / 2 * scaleNum, (609 + 30) * scaleNum, 530 *scaleNum); // marginleft:182rpx;maxWidth:148 最大宽度// 6.icon res = await drawCanvas("https://cdn.froglesson.com/static/cert/index_logo.png")ctx.drawImage(res.tempFilePath, 210 * scaleNum, (716 + 30) * scaleNum, 80 * scaleNum,80 * scaleNum)// 7.qrcoderes = await drawCanvas(this.qrCode);ctx.drawImage(res.tempFilePath, 299 * scaleNum, (716 + 30) * scaleNum, 80 * scaleNum,80 * scaleNum)// 8.底部titlelet appName = "XXXXAPPNAMEXXXXXX"ctx.setFontSize(24 * scaleNum) //字体大小ctx.setFillStyle('#fff') //文字颜色ctx.setTextAlign('center') //文本左对其ctx.fillText(appName, 590 / 2 * scaleNum, (801 + 50) * scaleNum, 530 *scaleNum); // marginleft:182rpx;maxWidth:148 最大宽度// 完成绘制 - 执行保存ctx.draw()console.log('绘制完毕')uni.setStorageSync('openSaveShareImage', true)this.saveOver()},saveOver() {console.log("绘制----------end")setTimeout(() => {uni.canvasToTempFilePath({canvasId: 'sharePic',destWidth: this.boxDetail.width * 2, //展示图片尺寸=画布尺寸1*像素比2destHeight: this.boxDetail.height * 2,quality: 1,fileType: 'png',success: (result) => {console.log(result.tempFilePath)uni.saveImageToPhotosAlbum({filePath: result.tempFilePath,success: function() {uni.hideToast()uni.showToast({icon: 'none',title: "图片已下载至【相册】,请打开【相册】查看", // res.tempFilePath});},fail: function() {uni.hideToast()uni.showToast({icon: 'none',title: "保存失败", // res.tempFilePath});}})},}, this);}, 1000)},