开发环境是在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就是多从转换图片的问题,所以上方代码新增了两个变量elBgIdStart
和elIdStart
予以限制。
注意事项:
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,上面代码没加。
另如果使用的是网络图片,参考这位大哥
有问题欢迎指正,也希望一起交流~