likes
comments
collection
share

抽奖大转盘迁移记录

作者站长头像
站长
· 阅读数 4

前言

前端时间迁移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,实现了弹框效果;

修改前:

<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>

修改后:

<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写的转盘不生效,并提示较多报错,通过查看资料,最终在微信官方的文档中发现了这样一篇文章:

旧版 Canvas 迁移指南

参照此篇文章,在代码中对以下代码做了修改:

3.1 修改 WXML

<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

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()不再支持

// ctx.draw(); // 不在支持

迁移指南中提到:

旧版 canvas 接口绘制需要调用 CanvasContext.draw 才会进行绘制,并且绘制过程是异步的,需要等待绘制完成回调才能进行下一步操作。

新版 Canvas 2D 接口不再需要调用 draw 函数,所有绘制方法都会同步绘制到画布上。

需要注意的是 CanvasContext.draw 函数第一个参数控制在绘制前是否保留上一次绘制(默认值为 false,即不保留),若设置为 false,则迁移至新接口后,需要在绘制前通过 clearRect 清空画布。

3.3.2 canvasToTempFilePath 属性修改

迁移指南中提到:旧版 canvas 接口传入 canvas-id;新版 Canvas 2D 接口需要直接传入 Canvas实例 。

  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等类似设置样式的方法不再支持,需要做相应的修改;

// ctx.setLineWidth(_w); 
ctx.lineWidth= _w;

// that.canvasCtx.setStrokeStyle("#199301"); //设置画图线的颜色 
that.canvasCtx.strokeStyle="#199301"; //设置画图线的颜色

4、使用uni.createSelectorQuery()提示 TypeError: Cannot read property 'node' of null

原代码:

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打印值:

initCanvasParams---res= [null]

期间将此方法放到 mounted 调用 甚至使用 setTimeout 加了个一两秒的延时,仍然不生效;

后想到当前实例下或许有createSelectorQuery,尝试了,果然如此。

修改后代码:

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打印值:

initCanvasParams---res= [{"width":260,"height":260,"nodeCanvasType":"2d","node":{"id":815262}}]

5、 Canvas渲染在微信开发者工具中正常,在真机中显示被裁切了

经过各种修修补补,最终大转盘页面终于不报错,正常显示出来了,终于松了一口气;谁知,扫码在手机上打开后,原本在开发者工具中正常展示的大转盘,原本是圆形的,硬生生被砍掉了只剩下左半边的一部分,右侧的直接没有了。它爷爷的大腿的,什么情况?

复查了一便又一遍代码,就在canvasStyle中,canvas也赋值了宽高了啊,

<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就可以了,好吧,试一下

<canvas
    type="2d" 
    id="turnplateCanvas"
    :style="canvasStyle"
    :width="`${canvasConfig.width}px`"
    :height="`${canvasConfig.height}px`"
>
</canvas>

跑起来,还是一样,不信邪,清除缓存,继续跑了几遍,还是老样子。

心中无数个草泥马浮现,没法,还得继续找原因,又去查看文档,最终又回到了

旧版 Canvas 迁移指南

里面的第三步提到了画布大小初始化

抽奖大转盘迁移记录

它为什么要在这里提到绘制画布大小呢,难不成老版本直接在样式或者属性中设置宽高都不起作用了(实测不生效),只能通过这种方式,在这里重新赋值一遍宽高才可以。抱着试一试的态度,我在createSelectorQuery的exec回调函数中,对canvas对象重新赋值了宽高

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,

/**
 * 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对象的宽度,如下:

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变化了,也会给我们开发带来意想不到的后果。遇到问题的时候,不要着急,要相信自己,在我们一次次尝试的失败过程中,总有柳暗花明的那一天。