解决uniapp使用canvas层级过高问题并记录踩坑过程

开发环境是在uniapp的微信小程序中使用uview1.0版本的圆形进度条u-circle-progress组件,在组件中更改canvas代码实现的。

一、开发遇到的问题

页面需要两个圆形进度条,样式改好后发现canvas层级过高无法被uview的自定义导航栏u-navbar遮盖。

二、解决办法

第一个是更改z-index无效,导航栏和canvas该哪个都不行,因为canvas原生组件的层级非常高;

第二个是使用cover-view标签,说是小程序层级最高的标签,微信官方链接在这里,尝试了发现两个问题:一是cover-view和canvas可能层级相同,使用cover-view重写自定义导航栏后,滚动的canvas进度条到顶部依然会遮盖导航栏 ;二是cover-view是原生组件上的文本视图容器,uniapp官方链接在这里,注意事项特多,有一定局限性,而且第一次使用有些举步维艰,不仅要考虑导航栏、状态栏的高度,还要重写样式(看别人说对icon不友好),要满足现有的自定义导航栏样式要求很费事费力

第三个就是现在用的比较多的办法:将canvas标签替换为图片

实现也不难,但是需要需要一些方面,先上代码:

js 复制代码
setTimeout(() => {
	var that = this
	uni.canvasToTempFilePath({
		canvasId: this.elBgId,
		success: (res) => {
			console.log(res.tempFilePath)
		},
		fail: (err) => {
			console.error(err, 'err')
		}
	}, that)
}, 1000)

这里两个踩坑:第一个是要加延时器 ,必须要等到canvas绘制完成后才可以调用API转成本地图片,否则绘制不完整。 另一个是调用API需要传第二个参数that参考这位大哥的文档,这里就是因为在组件中使用时,未传参一直报canvasToTempFilePath:fail fail canvas is empty的错误,看其他人使用很多并未传这个参数,所以这个就看个人需要吧。

另一个坑是拿到图片路径后,在canvas标签切换成image时,不管是使用v-if还是v-show,这里都会出现替换时的闪烁的片段 ,主要是canvas消失和image渲染时的空白时段,这个没有完全解决(可能是我这里加了阴影的原因),不过优化了一下,思路就是转化完成图片后,将图片元素和canvas元素同时渲染,然后在单独将canvas元素给隐藏,如此闪烁的瞬间没有那么明显,我这里看起来就只是类似心脏跳动了一下,个人觉得看起来还可以。上代码:

yaml 复制代码
//data()中新增了这些变量
    elBgIdShowOne: true,
    elBgIdShowTwo: false,
    elBgIdUrl: '',
    elBgIdStart: true,
    elIdShowOne: true,
    elIdShowTwo: false,
    elIdUrl: '',
    elIdStart: true,
js 复制代码
<canvas v-show="elBgIdShowOne" class="u-canvas-bg" 
    :canvas-id="elBgId" :id="elBgId" 
    :style="{width: widthPx + 'px',height: widthPx + 'px'}"></canvas>
	
<image v-show="elBgIdShowTwo" :src="elBgIdUrl" style="width: 100%;height: 100%;position: absolute;top: 0;left: 0;">
</image>

<canvas v-show="elIdShowOne" class="u-canvas" 
    :canvas-id="elId" :id="elId" 
    :style="{width: widthPx + 'px',height: widthPx + 'px'}"></canvas>
	
<image v-show="elIdShowTwo" :src="elIdUrl" style="width: 100%;height: 100%;position: absolute;top: 0;left: 0;">
</image>

因为uview圆形进度条的组件是使用两个canvas元素,一个作为背景条,一个进度条,所以需要在两个绘制canvas的方法里面都加转化canvas为图片的办法:

js 复制代码
//第一个是将drawProgressBg()方法中的ctx.draw()替换为下面代码
ctx.draw(false, (res) => {
	if (res.errMsg == "drawCanvas:ok") {
		if (this.elBgIdStart) {
			this.elBgIdStart = false
			setTimeout(() => {
				var that = this
				uni.canvasToTempFilePath({
					canvasId: this.elBgId,
					success: (res) => {
						// 在H5平台下,tempFilePath 为 base64
						console.log(res.tempFilePath)
						this.elBgIdShowTwo = true
						setTimeout(() => {
							this.elBgIdShowOne = false
						}, 100)
						this.elBgIdUrl = res.tempFilePath
					},
					fail: (err) => {
						console.error(err, 'err')
					}
				}, that)
			}, 1000)
		}
	}
});
js 复制代码
//第二个是将drawCircleByProgress()方法中的ctx.draw()替换为下面代码
ctx.draw(false, (res) => {
	if (res.errMsg == "drawCanvas:ok") {
		if (this.elIdStart) {
			this.elIdStart = false
			setTimeout(() => {
				var that = this
				uni.canvasToTempFilePath({
					canvasId: this.elId,
					success: (res) => {
						// 在H5平台下,tempFilePath 为 base64
						console.log(res.tempFilePath)
						this.elIdShowTwo = true
						setTimeout(() => {
							this.elIdShowOne = false
						}, 100)
						this.elIdUrl = res.tempFilePath
					},
					fail: (err) => {
						console.error(err, 'err')
					}
				}, that)
			}, 1000)

	}
});

之所以是在draw()中调用API转图片是因为官方说draw()有绘制完成后执行的回调函数,但其实并不是最终完成后的回调,每次绘制进行中就会执行一次这个回调,于是踩坑+1就是多从转换图片的问题,所以上方代码新增了两个变量elBgIdStartelIdStart予以限制。

注意事项:

1、此处的uni.canvasToTempFilePath()API并没有做宽高的限制 ,默认使用的是canvas的宽高,有额外需求的可以看此处官方给的属性

2、自定义内容的进度条组件中心的数字会被转化后的图片挤下去,图片也会变形,所以需要在使用进度条组件的父盒子加上相对定位,并要在image单独加绝对定位的样式。

3、如果使用的是canvas2d绘制的 ,在转化成图片的API参数中,不使用canvasId的字段,改成canvas,否则会报"canvasToTempFilePath:fail Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas or VideoFrame)'"的错误。

4、使用微信原生绘图的wx.createSelectorQuery().select('#myCanvas').fields({node: true,size: true}).exec(),只要在canvas绘制后面调用uni.canvasToTempFilePath()就好。

5、成功后记得清除延时器,两行代码clearTimeout()然后置为null,上面代码没加

另如果使用的是网络图片,参考这位大哥

有问题欢迎指正,也希望一起交流~

相关推荐
某公司摸鱼前端2 天前
推荐 uniapp 相对好用的海报生成插件
小程序·uni-app·canvas
&活在当下&5 天前
js 实现视频封面截图
前端·javascript·音视频·canvas
xachary10 天前
前端使用 Konva 实现可视化设计器(23)- 绘制曲线、属性面板
前端·canvas·konva
Jiaberrr10 天前
微信小程序教程:如何在个人中心实现头像贴纸功能
前端·微信小程序·小程序·canvas·头像
xiangxiongfly91510 天前
微信小程序-canvas
微信小程序·小程序·canvas
胡西风_foxww11 天前
canvas分享,从入门到入门。
javascript·教程·canvas·入门·分享
漠河愁1 个月前
pdf文件渲染到canvas
canvas·pdf.js·fabirc.js
xachary2 个月前
前端使用 Konva 实现可视化设计器(21)- 绘制图形(椭圆)
canvas·konva
x007xyz2 个月前
前端纯手工绘制音频波形图
前端·音视频开发·canvas