IOS 17.4 微信小程序 canvas 签字连笔卡顿 如何解决 ?

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

相关推荐
kinlon.liu16 分钟前
Web应用安全实用建议
前端·网络·网络协议·安全·centos
narukeu20 分钟前
理解 React 的严格模式
前端·javascript·react.js
Bee.Bee.22 分钟前
移动端如何调试本地运行的前端项目
前端
Tech Synapse24 分钟前
java 如何暴露header给前端
java·开发语言·前端
卓卓没头发33 分钟前
掌握Vue插槽:创建灵活且可复用的组件
前端·javascript·vue.js
白臻41 分钟前
小程序 npm 支持
前端·小程序·npm
lyllovelemon44 分钟前
🍭🍭🍭五分钟带你掌握next国际化最佳实践
前端·react.js·面试
用户4099322502122 小时前
Nuxt框架中内置组件详解及使用指南(三)
前端·vue.js·nuxt.js
化作繁星2 小时前
vue3项目图片压缩+rem+自动重启等plugin使用与打包配置
前端·vue·vite
u0104058362 小时前
构建可扩展的Java Web应用架构
java·前端·架构