解决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,上面代码没加

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

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

相关推荐
又吹风_Bassy19 小时前
WhatsCanvas教程-第三章:画笔 Paint 详解
canvas·opengl·skia·2d渲染库·nanovg·whatscanvas·2d图形库
又吹风_Bassy1 天前
WhatsCanvas教程-第二章:基础图形绘制
canvas·opengl·skia·2d渲染库·nanovg·whatscanvas·2d图形库
我就是DaLing呀!1 天前
vue3 + 独立的数据管理 Store实现视频剪辑功能
前端·typescript·vue3·canvas·store
牧艺5 天前
登录页还在用渐变?我一口气做了 5 个能摸的背景动效:吹蒲公英、滴墨染水、点熔岩闷裂
前端·canvas·交互设计
Highcharts.js5 天前
Highcharts 渲染方式解析:SVG 默认渲染与 Boost 模块的 WebGL 加速,以及它与其他可视化库的不同
svg·webgl·可视化·canvas·highcharts·渲染方式
AndrewHZ8 天前
图像处理入门018 | 简易图像浏览器开发:用 OpenCV + Tkinter 打造你的第一个图像 GUI 工具
图像处理·人工智能·opencv·canvas·tkinter·事件绑定
进哥AI研习社11 天前
WebGL 与视频背景——首屏视觉层的性能博弈
性能优化·webgl·gpu·canvas·shader·帧率·视频背景
MicrosoftReactor14 天前
技术速递|Canvas 如何让 Agentic Workflow 更可见、可控、更高效
ai·agent·canvas
又吹风_Bassy20 天前
介于 NanoVG 和 Skia 之间:C++17 可嵌入式 2D 渲染库 WhatsCanvas 全面介绍
canvas·skia·2d渲染库·nanovg·开源渲染库·whatscanvas·whatsui
今日无bug21 天前
HTML5 Canvas:从画图到游戏开发
前端·canvas