1. Bug初显
某前端程序猿 周日(2024-03-10),被客服疯狂 @ 系统出问题了。 如同 草履虫一样 😂
2. Bug 定位
微信小程序在最新苹果系统 ios 17.4 中, touchmove方法 , 无法持续触发 具体原因未知 🤣
js
<canvas
class="canvas"
id="canvas"
canvas-id="canvas"
disable-scroll="true"
@touchstart="canvasStart"
@touchmove="canvasMove"
@touchend="canvasEnd"
touchcancel="canvasEnd"
@error="canvasIdErrorCallback">
</canvas>
3. 在混乱中, 线索初显
仔细阅读了小程序的官方组件canvas
发现有一个代码片段 如下图 可以持续触发 touchmove方法, 到这好像找到解决方案
4. 在希望中, 改造签字版
出现问题的源码如下,方便jy们 排查
html
<template>
<view class="applicationForm_box">
<div class="canvas_box">
<canvas
class="canvas"
id="canvas"
canvas-id="canvas"
disable-scroll="true"
@touchstart="canvasStart"
@touchmove="canvasMove"
@touchend="canvasEnd"
touchcancel="canvasEnd"
@error="canvasIdErrorCallback">
</canvas>
<div class="signIsFinshed_box">
<div class="commonSmallBtn" @click="cleardraw" style="margin-right:82px;">重签</div>
<div class="commonSmallBtn" @click="getimg">提交</div>
</div>
</div>
<van-toast id="van-toast"></van-toast>
</view>
</template>
<script>
var context = null;// 使用 wx.createContext 获取绘图上下文
var isButtonDown = false;
var arrx = [];
var arry = [];
var arrz = [];
var canvasw = 0;
var canvash = 0;
//获取系统信息
wx.getSystemInfo({
success: function (res) {
canvasw = res.windowWidth;//设备宽度
canvash = res.windowHeight; //设备高度
}
});
export default {
data(){
return {
show: false, // 是否显示canvas
canvasWidth: 350, // 默认canvas宽高
canvasHeight: 1080,
screenWidth: null, // 设备宽度
screenHeight: null, // 设备宽度
name: '',
pic: '',
chapter: '',
widget: null,
imageUrl:'',
isFinshedSign:false,
checkRadio:false,
formData:{}
}
},
onLoad(options){
this.initAllData()
},
onUnload() {
this.cleardraw()
},
methods: {
initAllData(){
context = wx.createCanvasContext('canvas');
context.beginPath()
context.setStrokeStyle('#000000');
context.setLineWidth(8);
context.setLineCap('round');
context.setLineJoin('round');
context.setFontSize(24)
context.setFillStyle('#9A9A9A')
context.setTextAlign('center')
context.fillText('签字区域',0.56*canvash,0.4*canvasw)
context.draw()
},
canvasIdErrorCallback: function (e) {
console.error(e.detail.errMsg)
},
//绘制开始
canvasStart: function (event) {
isButtonDown = true;
arrz.push(0);
arrx.push(event.changedTouches[0].x);
arry.push(event.changedTouches[0].y);
},
//绘制过程
canvasMove: function (event) {
if (isButtonDown) {
arrz.push(1);
arrx.push(event.changedTouches[0].x);
arry.push(event.changedTouches[0].y);
};
for (var i = 0; i < arrx.length; i++) {
if (arrz[i] == 0) {
context.moveTo(arrx[i], arry[i])
} else {
context.lineTo(arrx[i], arry[i])
};
};
context.clearRect(0, 0,canvash+200, canvasw);
context.setStrokeStyle('#000000');
context.setLineWidth(6);
context.setLineCap('round');
context.setLineJoin('round');
context.stroke();
context.draw(false);
},
canvasEnd (event) {
isButtonDown = false;
},
cleardraw () {
//清除画布
arrx = [];
arry = [];
arrz = [];
context.clearRect(0, 0, canvash+200, canvasw);
context.setFontSize(24)
context.setFillStyle('#9A9A9A')
context.setTextAlign('center')
context.fillText('签字区域',0.56*canvash,0.4*canvasw)
context.draw(true);
},
},
}
</script>
<style scoped>
.applicationForm_box {
box-sizing: border-box;
background: #F5F5F5;
}
.canvas {
width: 100vw;
height: 80vh;
background-color:#F9F9F9;
}
.canvas_box {
box-sizing: border-box;
position: relative;
background: #fff;
}
.signIsFinshed_box {
display: flex;
align-items: center;
justify-content: center;
}
.commonSmallBtn {
border: 1px solid #3B66FF;
padding: 4rpx;
box-sizing: border-box;
width: 88px;
white-space: nowrap;
text-align: center;
background: #3B66FF;
color: #fff;
margin-top: 16rpx;
border-radius: 4px;
}
</style>
使用 canvas 2d ,就可以持续触发 touchmove
讲canvas 的type属性定义为 2d
js
<canvas
class="canvas"
id="canvas"
type="2d"
canvas-id="canvas"
disable-scroll="true"
@touchstart="canvasStart"
@touchmove="canvasMove"
@touchend="canvasEnd"
touchcancel="canvasEnd"
@error="canvasIdErrorCallback">
</canvas>
需要注意的是 初始化签字版的方法,与之前完全不同
js
wx.createSelectorQuery()
.select('#canvas') // 在 WXML 中填入的 id
.fields({ node: true, size: true })
.exec((res) => {
// Canvas 对象
canvas = res[0].node
// 渲染上下文
context = canvas.getContext('2d')
console.log('context',context);
// Canvas 画布的实际绘制宽高
const width = res[0].width
const height = res[0].height
// 初始化画布大小
const dpr = wx.getWindowInfo().pixelRatio
canvas.width = width * dpr
canvas.height = height * dpr
context.scale(dpr, dpr)
// 初始化
context.beginPath()
arrx = [];
arry = [];
arrz = [];
context.strokeStyle='#000000';
context.lineWidth=6;
context.lineCap="round";
context.lineJoin="round";
context.font= "24px sans-serif"
context.fillStyle="#9A9A9A"
context.textAlign="center"
context.fillText('签字区域',0.56*canvash,0.4*canvasw)
})
设置签字笔画的粗细和字体啥的都与之前不一样哦 😘 有需要可以打印 context 具体的参数,看看如何使用 😎
js
// 现在的做法
context.strokeStyle='#000000';
context.lineWidth=6;
context.lineCap="round";
context.lineJoin="round";
context.font= "24px sans-serif"
context.fillStyle="#9A9A9A"
context.textAlign="center"
// 之前的做法
context.setStrokeStyle('#000000');
context.setLineWidth(8);
context.setLineCap('round');
context.setLineJoin('round');
context.setFontSize(24)
context.setFillStyle('#9A9A9A')
context.setTextAlign('center')
context.fillText('签字区域',0.56*canvash,0.4*canvasw)
5. 大功告成,补丁完成
丝滑无比
🎉🎉🎉
6. 贡献整体补丁代码
js
<template>
<view class="applicationForm_box">
<div class="canvas_box">
<canvas
class="canvas"
id="canvas"
type="2d"
canvas-id="canvas"
disable-scroll="true"
@touchstart="canvasStart"
@touchmove="canvasMove"
@touchend="canvasEnd"
touchcancel="canvasEnd"
@error="canvasIdErrorCallback">
</canvas>
<div class="signIsFinshed_box">
<div class="commonSmallBtn" @click="cleardraw" style="margin-right:82px;">重签</div>
<div class="commonSmallBtn" @click="getimg">提交</div>
</div>
</div>
<van-toast id="van-toast"></van-toast>
</view>
</template>
<script>
var context = null;// 使用 wx.createContext 获取绘图上下文
var isButtonDown = false;
var canvas =null
var arrx = [];
var arry = [];
var arrz = [];
var canvasw = 0;
var canvash = 0;
//获取系统信息
wx.getSystemInfo({
success: function (res) {
canvasw = res.windowWidth;//设备宽度
canvash = res.windowHeight; //设备高度
}
});
export default {
data(){
return {
show: false, // 是否显示canvas
canvasWidth: 350, // 默认canvas宽高
canvasHeight: 1080,
screenWidth: null, // 设备宽度
screenHeight: null, // 设备宽度
name: '',
pic: '',
chapter: '',
widget: null,
imageUrl:'',
isFinshedSign:false,
checkRadio:false,
formData:{}
}
},
onLoad(options){
let that = this
wx.createSelectorQuery()
.select('#canvas') // 在 WXML 中填入的 id
.fields({ node: true, size: true })
.exec((res) => {
// Canvas 对象
canvas = res[0].node
// 渲染上下文
context = canvas.getContext('2d')
console.log('context',context);
// Canvas 画布的实际绘制宽高
const width = res[0].width
const height = res[0].height
// 初始化画布大小
const dpr = wx.getWindowInfo().pixelRatio
canvas.width = width * dpr
canvas.height = height * dpr
context.scale(dpr, dpr)
// 初始化
context.beginPath()
arrx = [];
arry = [];
arrz = [];
context.strokeStyle='#000000';
context.lineWidth=6;
context.lineCap="round";
context.lineJoin="round";
context.font= "24px sans-serif"
context.fillStyle="#9A9A9A"
context.textAlign="center"
context.fillText('签字区域',0.56*canvash,0.4*canvasw)
})
},
onUnload() {
this.cleardraw()
},
methods: {
canvasIdErrorCallback: function (e) {
console.error(e.detail.errMsg)
},
//绘制开始
canvasStart: function (event) {
isButtonDown = true;
arrz.push(0);
arrx.push(event.changedTouches[0].x);
arry.push(event.changedTouches[0].y);
},
//绘制过程
canvasMove: function (event) {
if (isButtonDown) {
arrz.push(1);
arrx.push(event.changedTouches[0].x);
arry.push(event.changedTouches[0].y);
};
context.clearRect(0, 0,canvash+200, canvasw);
context.beginPath();
for (var i = 0; i < arrx.length; i++) {
if (arrz[i] == 0) {
context.moveTo(arrx[i], arry[i])
} else {
context.lineTo(arrx[i], arry[i])
};
};
context.strokeStyle='#000000';
context.lineWidth=6;
context.lineCap="round";
context.lineJoin="round";
context.stroke();
},
canvasEnd (event) {
isButtonDown = false;
},
cleardraw () {
//清除画布
arrx = [];
arry = [];
arrz = [];
context.clearRect(0, 0, canvash+200, canvasw);
context.font= "24px sans-serif"
context.fillStyle="#9A9A9A"
context.textAlign="center"
context.fillText('签字区域',0.56*canvash,0.4*canvasw)
},
}
</script>
<style scoped>
.applicationForm_box {
box-sizing: border-box;
background: #F5F5F5;
}
.canvas {
width: 100vw;
height: 80vh;
background-color:#F9F9F9;
}
.canvas_box {
box-sizing: border-box;
position: relative;
background: #fff;
}
.signIsFinshed_box {
display: flex;
align-items: center;
justify-content: center;
}
.commonSmallBtn {
border: 1px solid #3B66FF;
padding: 4rpx;
box-sizing: border-box;
width: 88px;
white-space: nowrap;
text-align: center;
background: #3B66FF;
color: #fff;
margin-top: 16rpx;
border-radius: 4px;
}
</style>
7. 结语
愿世界和平,再无bug