解决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 天前
cesiusm实现 多图例展示+点聚合(base64图标)
css3·canvas·base64·cesium·animation
余生H9 天前
即时可玩web小游戏(二):打砖块(支持移动端版) - 集成InsCode快来阅读并即时体验吧~
前端·javascript·inscode·canvas·h5游戏
普兰店拉马努金16 天前
【Canvas与图标】牛皮纸文件袋图标
canvas·图标·文件袋·牛皮纸
德育处主任18 天前
前端啊,拿Lottie炫个动画吧
前端·svg·canvas
GDAL19 天前
深入剖析Canvas的getBoundingClientRect:精准定位与交互事件实现
canvas
剑亦未配妥21 天前
使用js和canvas、html实现简单的俄罗斯方块小游戏
前端·javascript·canvas·1024程序员节
howard200523 天前
2.1 HTML5 - Canvas标签
html5·canvas
普兰店拉马努金1 个月前
【Canvas与标牌】立入禁止标牌
canvas·警示·标识·立入禁止
Anlige1 个月前
Javascript:使用canvas画二维码矩阵
javascript·canvas·qrcode
牛老师讲GIS1 个月前
分享一个从图片中提取色卡的实现
canvas·提取颜色