优化版小程序canvas,增加失败逻辑,及完善文字

wxml

<view class="shareBox" style="backgound:{{isShow ? '#000' : '#fff'}}" wx:if="{{isShow && canvasList}}">
    <canvas canvas-id="firstCanvas" class="canvas myCanvas" style="width:{{canvasWidth}}px;height:{{canvasHeight}}px;margin-top:calc((100vh - 54px - {{canvasHeight}}px) / 2)"></canvas>
    <button class="saveImg {{iphoneX ? 'iphonex' : ''}}" bindtap="saveImage" disabled="{{btnShow}}">保存图片</button>
</view>
<view wx:else class="refresh">
    <button size="default" type="primary" bindtap="readyCanvas">重新加载</button>
</view>

  

js

import regeneratorRuntime from "../../../lib/regenerator-runtime/runtime";
let ctx = false, crown = 0, widFit = 0	// ctx canvas对象, crown生成图的宽高比, widFit当然布局下与需生成图寛比, heiFit高度比
Component({
	externalClasses:['myCanvas'],
	properties: {
		canvasList: {
			type: Array,
			value: []
		},
		getShareWidth: { //想得到的分享图宽度
			type: Number,
			value: 1080
		},

		getShareHeight: { //想得到的分享图高度
			type: Number,
			value: 1900
		}

	},

	/**
	 * 组件的初始数据
	 */
	data: {
		canvasWidth: 375, //屏幕宽度
		canvasHeight: 375, //屏幕高度
		isShow: true, //canvas组件默认显示
		iphoneX: false,	//适配机型
		btnShow: false,	//阻止事件多次触发
	},

	/**
	 * 组件的方法列表
	 */
	methods: {
		async canvasStart() {
			this.drawbackColor()
			const canvasList = this.data.canvasList
			await canvasList.map( (v,k)=>{
				if(v.type === 'backImage'){
					this.drawBackImg(v.url)
				}else if(v.type === 'image'){
					this.drawContentImg(v)
				}else if(v.type === 'line'){
					this.drawLine(v.drawLine)
				}else{
					this.drawText(v.text,v.drawText)
				}
			})

			ctx.draw()
			wx.hideLoading()
			// this.saveImage()
		},

		//画默认白底色
		drawbackColor(){
			const { canvasWidth, canvasHeight } = this.data
			ctx.save()
			ctx.rect(0, 0, canvasWidth, canvasHeight)
			ctx.setFillStyle('white')
			ctx.fill()
		},

		// 画背景图
		drawBackImg(url) {
			// console.log('drawBackImg',url)
			const {
				canvasWidth,
				canvasHeight
			} = this.data
			ctx.save()
			ctx.drawImage(url, 0, 0, canvasWidth, canvasHeight)
		},

		// 画图不用裁剪(查看小程序canvas api 文档 https://developers.weixin.qq.com/miniprogram/dev/api/CanvasContext.drawImage.html)
		drawImg(url, drawArguments) {
			const arg = Object.keys(drawArguments)
			if (arg.length == 8) {

				const { sx, sy, sWidth, sHeight, dwX, dwY, dWidth, dHeight } = drawArguments
				ctx.drawImage(url, sx * widFit, sy * widFit, sWidth * widFit, sHeight * widFit, dwX * widFit, dwY * widFit, dWidth * widFit, dHeight * widFit)

			} else if (arg.length == 4) {
				
				const { dwX, dwY, dWidth, dHeight } = drawArguments
				ctx.drawImage(url, dwX * widFit, dwY * widFit, dWidth * widFit, dHeight * widFit)

			} else if (arg.length == 2) {

				const { dwX, dwY } = drawArguments
				ctx.drawImage(url, dwX * widFit, dwY * widFit)

			} else {

				wx.showToast({
					title: '背景图传入参数有误,请确认无误后再进行操作',
					icon: 'none'
				})

			}

    	},
    
		// 画内容图 clip裁剪
		drawContentImg(val){
			const { url, clip, drawArguments, drawArc} = val
			
			if(clip){		//裁剪流图片
				if(!url || !drawArguments || !drawArc){
					wx.showToast({
						title: '请确认drawContentImg参数无误',
						icon: 'none'
					})
					return;
				}

				const {
					x,
					y,
					radius
				} = drawArc
				if ((x || x === 0) && (y || y === 0) && (radius || radius === 0)) {
					const {
						x,
						y,
						radius,
						startRadian = 0,
						endRadian = 2 * Math.PI
					} = drawArc
					ctx.save()
					ctx.beginPath();
					ctx.arc(x * widFit, y * widFit, radius * widFit, startRadian, endRadian)  // arc(x坐标,y坐标,radius半径,startRadian起始弧度/单位弧度(默认在3点钟方向),endRadian终止弧度)
					ctx.clip()
					this.drawImg(url, drawArguments)
					ctx.restore()
				} else {
					wx.showToast({
						title: '画圆参数有误',
						icon: 'none'
					})
				}
			} else {
				ctx.save()
				this.drawImg(url, drawArguments)
				ctx.restore()
			}
			
		},

		// 画文字
		drawText(text,drawText){
			const canvasWidth = this.data.canvasWidth
			const exg = /[a-zA-Z0-9]/g
			const { x, y, bold, maxWidth = canvasWidth, fontSize = 14, lineHeight = 0, color = 'white', textAlign = 'left' } = drawText  // text文字内容, x画布X坐标, y画布y坐标, maxWidth最大宽度, fontSize字体大小, color文字颜色
			
			const patchTextHeight =  fontSize ? fontSize : lineHeight
			if(!text || (!x && x !== 0) || (!y && y !== 0)){
				wx.showToast({
					title: '文字传入参数有误',
					icon: 'none'
				})
				return
			}
			ctx.save()
			ctx.setFillStyle(color)		//设置文字颜色
			ctx.setTextAlign(textAlign)
			if(bold){
				ctx.font = `${fontSize}px bold PingFangSC-Medium`	//设置文字样式
			}else{
				ctx.setFontSize(fontSize)	//设置文字字体
			}

			const measure = ctx.measureText(text).width	//测量文本宽度
			const scale = Math.ceil(measure / (maxWidth * widFit))	//scale<1则 maxWidth>measure,1 <= scale < 2 则 maxWidth >= measure/2,scale >= 2 则 maxWidth <= measure / 2 
			let arr = [], count = 0
			 
			if(scale >= 2){
				let fontNum = Math.floor((maxWidth / fontSize) * widFit) //每行最多字体个数
				let patchVal = 0, patchY = y + patchTextHeight
				for(var i = 0; i < scale; i++){
					arr[i] = text.substr(patchVal,fontNum)
					count = arr[i].match(exg) ? arr[i].match(exg).length / 2 - 1 : 0
					// console.log('正则匹配:',ctx.measureText('和').width,ctx.measureText('B').width,ctx.measureText('a').width,ctx.measureText(1).width)
					fontNum += count 
					if(i < scale - 1){
						ctx.fillText(text.substr(patchVal,fontNum), x * widFit, patchY * widFit)
						patchVal += fontNum
						patchY += patchTextHeight
					}else{
						arr[i] = text.substr(patchVal)
						ctx.fillText(text.substr(patchVal), x * widFit, patchY * widFit)	//画最后剩下的内容
						console.log('arr:',arr)
					}
				}
			}else{
				ctx.fillText(text, x * widFit, (y + patchTextHeight)* widFit);
				console.log('felltext:',text,x*widFit,y*widFit)
			}

			ctx.restore()
		},

		// 画线
		drawLine(drwaLine){
			const { sx, sy, ex, ey, lineWidth = 1, color = '#EBEBEB' } = drwaLine
			if(!sx || !sy || !ex || !ey){
				wx.showToast({
					title: '画线参数有误',
					icon: 'none'
				})
				return
			}else{
				ctx.save()
				ctx.moveTo(sx, sy)	//线起点
				ctx.lineTo(ex, ey)	//线终点
				ctx.setLineWidth(lineWidth)	//线宽度
				ctx.setStrokeStyle(color)	//线颜色
				ctx.stroke()
				ctx.restore()
			}
		},
		
		// 保存图片
		saveImage(){
			wx.showLoading({
				title: '生成图片中...',
				mask: true
			})
			this.setData({
				btnShow: true
			})
			const { getShareWidth, getShareHeight } = this.data
			wx.canvasToTempFilePath({
				destWidth: getShareWidth * 5,
				destHeight: getShareHeight * 5,
				canvasId: 'firstCanvas',
				quality: 1,
				complete: fin=>{
				    // console.log('finish',fin)
				    if(fin.tempFilePath){
				        wx.saveImageToPhotosAlbum({
				            filePath: fin.tempFilePath,
				            success: (res)=>{
				                wx.showToast({
				                    title: '保存图片成功',
									icon: 'none',
									duration: 2000
								})
								this.setData({
									btnShow: false
								})
							}
				        })
				    }else{
							wx.showToast({
								title: '生成图片失败',
								icon: 'none'
							})
							this.setData({
								btnShow: false,
							})
						}
						
				}
			},this)
		},

		//转成本地图片
		getImages(url){
			return new Promise( (sovle,reject)=>{
				wx.getImageInfo({
					src: url,
					success: (res)=>{
						sovle(res.path) 
					},
					fail: (err)=>{
						wx.showToast({
							title: '网络不好,请稍后再试',
							icon: 'none'
						})
						this.setData({
							isShow: false
						})
					}
				});
			})
			
		},
		
		// 画图前准备工作
		async readyCanvas(){
			const { windowWidth, model } = wx.getSystemInfoSync();	// 获取屏幕宽高

			let { getShareWidth, getShareHeight, canvasList} = this.data
			crown = getShareWidth / getShareHeight	//分享图的宽高比
			const canvasHeight = windowWidth / crown

			if(model === 'iPhone X'){
				this.setData({
					canvasWidth: windowWidth,
					canvasHeight,
					iphoneX: true
				})
			}else{
				this.setData({
					canvasWidth: windowWidth,
					canvasHeight,
					iphoneX: false
				})
			}
			
			ctx = wx.createCanvasContext('firstCanvas',this) //把ctx赋值给全局变量
			widFit = windowWidth / getShareWidth   //宽比  (以px为单位)

			for(let i = 0; i < canvasList.length; i++){
				if(canvasList[i].url){
					canvasList[i].url = await this.getImages(canvasList[i].url)
					
					// console.log(i,canvasList[i].url)
				}
			}
			
			this.setData({
				canvasList,
				isShow: true
			},()=>{
				console.log(getShareWidth,getShareHeight,canvasList)
				this.canvasStart()
			})
		}

	},



	lifetimes: {

	},

	pageLifetimes: {
		show(){
			wx.showLoading({
				title: '加载中...',
				mask: true
			})
			this.readyCanvas()
		}
	}
})
		// let shareList = {		//传参例子
		// 	canvasList: [
		// 		{	
		// 			type: 'image', 
		// 			clip: false, 
		// 			url: courseInfo.bgImage,	//顶部背景
		// 			drawArguments:{ 
		// 				dwX: 0, 
		// 				dwY: 0, 
		// 				dWidth: 375, 
		// 				dHeight: 209 
		// 			},
		// 		},
		// 		{
		// 			type: 'text', 
		// 			text: courseInfo.courseTitle, 	
		// 			drawText:{ 
		// 				x: 20, 
		// 				y: 227, 
		// 				fontSize: 18,
		// 				color: '#303030',
		// 				bold: true
		// 			}
		// 		},
		// 		{	
		// 			type: 'image', 
		// 			clip: true, 
		// 			url: courseInfo.coachesInfo[0].avatar,	//教练头像
		// 			drawArguments:{ 
		// 				dwX: 20, 
		// 				dwY: 304, 
		// 				dWidth: 32, 
		// 				dHeight: 32 
		// 			},
		// 			drawArc:{ 
		// 				x: 36, 
		// 				y: 320, 
		// 				radius: 16, 
		// 			}
		// 		},
		// 		{
		// 			type:'line',
		// 			drawLine:{
		// 				sx: 20,	//开始x坐标
		// 				sy: 348,	//开始y坐标
		// 				ex: 355,	//结束x坐标
		// 				ey: 348,	//结束y坐标
		// 				lineWidth: 0.5,	//线宽度
		// 				color: '#ebebeb'	//线颜色
		// 			}
		// 		},
		// 	],
		// 	getShareWidth: 375,
		// 	getShareHeight: 463,
		// 	pageTitle: courseInfo.courseTitle
		// }

 

 wxss

 

.shareBox{
  width: 100vw;
  height: 100vh;
  background: #000;
  position: fixed;
  left: 0;
  top: 0;
}
.canvas{
  background: #fff;
}
.saveImg{
  width: 100%;
  height: 108rpx;
  line-height: 108rpx;
  background: #00C3AA;
  text-align: center;
  font-size: 30rpx;
  color: #FFFFFF;
  border-radius: 0;
  border: 0;
  position: absolute;
  left: 0;
  bottom: 0;
}
.refresh{
  width: 350rpx;
  height: 92rpx; 
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  margin: auto;
}
.iphonex{
  height: 172rpx;
}

 

  

 

转载于:https://www.cnblogs.com/jay-sans/p/10283873.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值