前言
前端时间迁移3年前用Canvas生成的抽奖大转盘项目(小程序页面),由于小程序框架改了,现在微信小程序Canvas的部分属性和方法也出现了变更,导致在迁移过程中原来正常运行的页面现在跑不起来,在一步步解决问题中遇到了较多的坑,特在此记录一下,就当是做个总结了。
项目背景
三年前公司老项目(贵州一码游小程序)采用 mpvue+vant/weapp 搭建,新项目 (新疆一码游小程序)使用 uniapp+uview 搭建。
填坑详情
1、vant组件不再使用
大转盘项目中vant组件只用到了van-popup,改为u-popup后,除了u-popup自带的白色背景,其余都正常;
我尝试了十八班武艺齐上阵,使用u-popup的custom-style属性或者css里面使用v-deep,最终都没能成功去除u-popup自带的白色背景,
最后彻底没招了,只好放弃 u-popup,改了结构,结合u-mask,实现了弹框效果;
修改前:
js
<template>
<van-popup
use-slot
:show="dialogVisible"
:custom-style="customStyle"
duration="168"
@close="close"
>
<div :class="'popup-dialog-wrap ' + typeStyle + ''">
具体弹框内容已省略
</div>
</van-popup>
</template>
<script>
export default {
computed: {
customStyle() {
return `width: ${this.width}%;background:none;`;
}
}
};
</script>
修改后:
js
<template>
<!-- <u-popup v-model="dialogVisible" :closeable="false" :custom-style="customStyle"
mode="center" @close="close"> -->
<!-- </u-popup> -->
<!-- 使用u-popup会自带背景色白色,使用custom-style或者v-deep等多方测试,都没法去除白色背景,没办法了,只好自定义UI,结合u-mask
-->
<u-mask :show="dialogVisible" :mask-click-able="false">
<div class="u-drawer-content" :style="customStyle">
<div :class="'popup-dialog-wrap ' + typeStyle + ''">
具体弹框内容已省略
</div>
</div>
</u-mask>
</template>
2、子组件中onLoad失效
由mpvue迁移到uniapp,原来项目子组件中写在onLoad中的代码不执行了(onLoad在子组件中不支持),改为created;
3、Canvas部分属性和方法不再支持或者发生了变化
小程序的 旧版Canvas接口不再维护,采用新版Canvas 2D接口
小程序中通过Canvas写的转盘不生效,并提示较多报错,通过查看资料,最终在微信官方的文档中发现了这样一篇文章:
参照此篇文章,在代码中对以下代码做了修改:
3.1 修改 WXML
js
<canvas :style="canvasStyle" canvas-id="turnplateCanvas"></canvas>
<!-- 修改为以下 -->
<canvas :style="canvasStyle" type="2d" id="turnplateCanvas"></canvas>
旧版 canvas 接口使用 canvas-id 属性唯一标识 canvas;新版 Canvas 2D 可直接使用 id 标识。
另外需要给 canvas 添加 type="2d" 属性标识为新版 Canvas 2D 接口。
指南中一开始就做了描述。
3.2 修改获取 CanvasContext
js
this.canvasCtx = wx.createCanvasContext("turnplateCanvas");
// 修改为以下
const that = this
uni.createSelectorQuery().select('#turnplateCanvas') .fields({
node: true,
size: true
}) .exec((res) => {
that.canvasNode = res[0].node
that.canvasCtx = this.canvasNode.getContext('2d')
// 以下为原来的代码逻辑 ...
}) },
这里改为 createSelectorQuery 后随之又引发了一个报错(TypeError: Cannot read property 'node' of null),导致代码进行不下去,后面章节将详细讲到,这里不再过多描述。
3.3 修改Canvas绘制方法
3.3.1 content.draw()不再支持
js
// ctx.draw(); // 不在支持
迁移指南中提到:
旧版 canvas 接口绘制需要调用 CanvasContext.draw 才会进行绘制,并且绘制过程是异步的,需要等待绘制完成回调才能进行下一步操作。
新版 Canvas 2D 接口不再需要调用 draw 函数,所有绘制方法都会同步绘制到画布上。
需要注意的是 CanvasContext.draw 函数第一个参数控制在绘制前是否保留上一次绘制(默认值为 false,即不保留),若设置为 false,则迁移至新接口后,需要在绘制前通过 clearRect 清空画布。
3.3.2 canvasToTempFilePath 属性修改
迁移指南中提到:旧版 canvas 接口传入 canvas-id;新版 Canvas 2D 接口需要直接传入 Canvas实例 。
js
let that = this;
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: that.canvasConfig.width,
height: that.canvasConfig.height,
canvasId: "turnplateCanvas",
success(res) {
wx.hideLoading();
that.canvasImg = res.tempFilePath;
},
});
// 修改后:
let that = this;
uni.canvasToTempFilePath({
x: 0,
y: 0,
width: that.canvasNode.width,
height: that.canvasNode.height,
canvas: that.canvasNode,
success(res) {
uni.hideLoading();
that.canvasImg = res.tempFilePath;
},
fail(res) {
console.log(res)
},
});
setLineWidth 、 setStrokeStyle等类似设置样式的方法不再支持,需要做相应的修改;
js
// ctx.setLineWidth(_w);
ctx.lineWidth= _w;
// that.canvasCtx.setStrokeStyle("#199301"); //设置画图线的颜色
that.canvasCtx.strokeStyle="#199301"; //设置画图线的颜色
4、使用uni.createSelectorQuery()提示 TypeError: Cannot read property 'node' of null
原代码:
js
initCanvasParams() {
// this.canvasCtx = uni.createCanvasContext("turnplateCanvas");
const that = this
uni.createSelectorQuery()
.select('#turnplateCanvas')
.fields({
node: true,
size: true
})
.exec((res) => {
console.log('initCanvasParams---res=', JSON.stringify(res))
that.canvasNode = res[0].node
// 更多代码已省略
})
},
报错截图:
console打印值:
js
initCanvasParams---res= [null]
期间将此方法放到 mounted 调用 甚至使用 setTimeout 加了个一两秒的延时,仍然不生效;
后想到当前实例下或许有createSelectorQuery,尝试了,果然如此。
修改后代码:
js
initCanvasParams() {
// this.canvasCtx = uni.createCanvasContext("turnplateCanvas");
const that = this
this.createSelectorQuery()
.select('#turnplateCanvas')
.fields({
node: true,
size: true
})
.exec((res) => {
console.log('initCanvasParams---res=', JSON.stringify(res))
that.canvasNode = res[0].node
// 更多代码已省略
})
},
修改后console打印值:
js
initCanvasParams---res= [{"width":260,"height":260,"nodeCanvasType":"2d","node":{"id":815262}}]
5、 Canvas渲染在微信开发者工具中正常,在真机中显示被裁切了
经过各种修修补补,最终大转盘页面终于不报错,正常显示出来了,终于松了一口气;谁知,扫码在手机上打开后,原本在开发者工具中正常展示的大转盘,原本是圆形的,硬生生被砍掉了只剩下左半边的一部分,右侧的直接没有了。它爷爷的大腿的,什么情况?
复查了一便又一遍代码,就在canvasStyle中,canvas也赋值了宽高了啊,
js
<canvas :style="canvasStyle" id="turnplateCanvas" > </canvas>
created() {
if (this.data && this.data.length > 0) {
uni.getSystemInfo({
success: (res) => {
// this.canvasConfig.pixelRatio = res.pixelRatio;
// this.canvasConfig.width = Math.floor((res.windowWidth * 69.6) / 100);
// this.canvasConfig.height = this.canvasConfig.width;
this.$set(this.canvasConfig, 'pixelRatio', res.pixelRatio)
this.$set(this.canvasConfig, 'width', Math.floor((res.windowWidth * 69.6) / 100))
this.$set(this.canvasConfig, 'height', this.canvasConfig.width)
this.canvasStyle = `width: ${this.canvasConfig.width}px; height: ${this.canvasConfig.height}px;`;
},
});
this.sectorNums = this.data.length;
} else {
return false;
}
},
难道是新版Canvas 不支持style了,百度各种搜索,有人说给Canvas加上width、height就可以了,好吧,试一下
js
<canvas
type="2d"
id="turnplateCanvas"
:style="canvasStyle"
:width="`${canvasConfig.width}px`"
:height="`${canvasConfig.height}px`"
>
</canvas>
跑起来,还是一样,不信邪,清除缓存,继续跑了几遍,还是老样子。
心中无数个草泥马浮现,没法,还得继续找原因,又去查看文档,最终又回到了
里面的第三步提到了画布大小初始化
它为什么要在这里提到绘制画布大小呢,难不成老版本直接在样式或者属性中设置宽高都不起作用了(实测不生效),只能通过这种方式,在这里重新赋值一遍宽高才可以。抱着试一试的态度,我在createSelectorQuery的exec回调函数中,对canvas对象重新赋值了宽高
js
initCanvasParams() {
// this.canvasCtx = uni.createCanvasContext("turnplateCanvas");
const that = this
this.createSelectorQuery()
.select('#turnplateCanvas')
.fields({
node: true,
size: true
})
.exec((res) => {
that.canvasNode = res[0].node
that.canvasNode.width = that.canvasConfig.width
that.canvasNode.height = that.canvasConfig.height
// 更多代码已省略
})
},
清缓存,重新跑了代码,打开手机查看,终于,我露出了微笑。
6、canvas画布不清晰解决的问题
终于给到测试环节了,原本松了一口气,结果没过多久,测试小姐姐提的bug又来了,Canvas生成的转盘图片不够清晰,比较模糊。
是这样吗,我在手机上也认真看了看,别说,还真是有点模糊,如果测试小姐姐不提出来,我也没有注意到,这还真是个问题,这样看起来真不咋地啊,怎么办,继续改呗。
收罗了百度上一些人提到的Canvas生成图片模糊的解决方案,尝试了下,都没成功,比如有人说在 uni.canvasToTempFilePath这里增加设置参数destWidth、destHeight,
js
/**
* canvas转换为图片显示,解决原生组件层级穿透引发的问题
* @param {Function} callback 结束后回调函数
* @return Void
*/
autoCanvarToImg() {
let that = this;
uni.canvasToTempFilePath({
x: 0,
y: 0,
width: that.canvasNode.width,
height: that.canvasNode.height,
destWidth: that.canvasConfig.width * (that.canvasConfig.pixelRatio || 2),
destHeight: that.canvasConfig.height * (that.canvasConfig.pixelRatio || 2),
canvas: that.canvasNode,
success(res) {
uni.hideLoading();
that.canvasImg = res.tempFilePath;
},
fail(res) {
console.log(res)
},
});
},
本人觉得可能也是,实际上测试下来,还是不行。
后来兜兜转转,我有回到了 旧版 Canvas 迁移指南
是不是画布大小初始化这段代码里面另含玄机呢,
参照着指南里面的代码,我修改了Canvas对象的宽度,如下:
js
created() {
if (this.data && this.data.length > 0) {
uni.getSystemInfo({
success: (res) => {
this.$set(this.canvasConfig, 'pixelRatio', res.pixelRatio)
this.$set(this.canvasConfig, 'width', Math.floor((res.windowWidth * 69.6) / 100))
this.$set(this.canvasConfig, 'height', this.canvasConfig.width)
this.canvasStyle = `width: ${this.canvasConfig.width}px; height: ${this.canvasConfig.height}px;`;
},
});
this.sectorNums = this.data.length;
} else {
return false;
}
},
/**
* 初始化Canvas
* @return Void
*/
initCanvasParams() {
// this.canvasCtx = uni.createCanvasContext("turnplateCanvas");
const that = this
this.createSelectorQuery()
.select('#turnplateCanvas')
.fields({
node: true,
size: true
})
.exec((res) => {
that.canvasNode = res[0].node
// 处理canvas画布不清晰解决的问题
// that.canvasNode.width = that.canvasConfig.width
// that.canvasNode.height = that.canvasConfig.height
that.canvasNode.width = res[0].width * that.canvasConfig.pixelRatio
that.canvasNode.height = res[0].height * that.canvasConfig.pixelRatio
that.canvasCtx = this.canvasNode.getContext('2d')
that.canvasCtx.scale(that.canvasConfig.pixelRatio, that.canvasConfig.pixelRatio)
// 更多代码已省略
})
},
改好后,自然是又一顿清除缓存,重新编译代码,谢天谢帝,终于是正常了。
总结
开发中总会遇到各种各样的坑,即便是原来OK的代码,环境变量,或者后来API变化了,也会给我们开发带来意想不到的后果。遇到问题的时候,不要着急,要相信自己,在我们一次次尝试的失败过程中,总有柳暗花明的那一天。