微信小程序实现电子签名

您所在的位置:网站首页 微信文件怎么签名 微信小程序实现电子签名

微信小程序实现电子签名

2023-10-13 07:23| 来源: 网络整理| 查看: 265

这是我参与8月更文挑战的第5天,活动详情查看: 8月更文挑战

背景

        因小程序业务需要用户签单确认订单,确认后生成图片回显。整理一下实现的过程和碰到的问题。 实现效果如图:

Screen_Recording_20210825-165559_WeChat 00_00_00-00_00_30.gif

代码实现 1.需求分析

        看的这个需求先把需求拆解一下,梳理一下实现的流程,参照上图,可以看出实现的大概步骤为:

页面横屏,页面布局,初始化Canvas容器; Canvas绘制操作提醒等文字; touchstart()事件创建路径的起点,将其坐标存入路径坐标数组中; touchmove()事件中不断往数组中存入路径坐标; touchend()事件清空数组,以便下一次的绘制; 2.手操实现 1.页面横屏,页面布局,初始化Canvas容器

        首先是第一步,因为是在小程序内,需要将手机横屏,以获取较大的空间,小程序内可以通过设置"pageOrientation": "landscape"实现,这种方式是最方便的,如果使用画布旋转的方式,最终生成的图片也是旋转的,还需要反向旋转回来。再一个小程序Canvas组件在(本文末尾会附上完整代码,这里只对js部分做说明)

initCanvas() { let { width, height } = this.data width = wx.getSystemInfoSync().windowWidth height = wx.getSystemInfoSync().windowHeight this.data.ctx = wx.createCanvasContext('canvas',this) this.setData({ width, height }) this.clearCanvas() },

因为ctx对象需要在多个方法里共享,所以可以将其定义在data中或者外部变量。

2.Canvas绘制操作提醒等文字

        这一步绘制的是提醒文字,文字左右居中显示可以设置setTextAlign('center')然后fillText时x轴的位置为宽度的一半。

clearCanvas() { this.data.drawCount = 0 this.data.ctx.setTextBaseline('top') this.data.ctx.setTextAlign('center') this.data.ctx.setFontSize(20) this.data.ctx.setFillStyle('#616165'); this.data.ctx.fillText("请在灰色区域内完成签名", this.data.width / 2, 30) this.data.ctx.draw(false) }, 3.touchstart()事件创建路径的起点,将其坐标存入路径坐标数组中

        上一步绘制的提醒文字类似于输入框的placeholder需要在Canvas被触发事件时清除掉,所以在这里判断一下如果是第一次触发的话就调用一下draw(false)事件,false参数代表覆盖上一次的绘制,传入true的话是保留上一次的绘制

catchtouchstart(e) { if (this.data.drawCount == 0) { this.data.ctx.draw(false) } this.data.drawCount++ this.data.points.push(e.changedTouches[0]) }, touch事件中的touches和changedTouches的不同: touches: 当前屏幕上所有触摸点的列表; changedTouches: 触发当前事件的触摸点的列表

例:两个手指同时触发事件,此时这两个属性内元素都是2个;两个手指先后触发事件,并且第一个手指不离开屏幕的话,当第二个手指触发事件时changedTouches内只有一个元素,为当前触发事件的触摸点信息,touches内有两个元素,为当前两个手指所在触摸点的信息。

4.touchmove()事件中不断往数组中存入路径坐标

        因为在手势移动时需要实时绘制路线,所以将draw()事件放在touchmove()事件内执行。setShadow()事件给轨迹设置阴影,使笔迹看起来比较圆滑。

catchtouchmove(e) { if (e.touches.length > 1) { return } this.data.points.push(e.changedTouches[0]) let points = this.data.points for (let i in points) { if (i == 0) { this.data.ctx.moveTo(points[0].clientX, points[0].clientY) } else { this.data.ctx.lineTo(points[i].clientX, points[i].clientY) } } this.data.ctx.setStrokeStyle('#000000'); this.data.ctx.setLineWidth(3); this.data.ctx.setShadow(0, 0, 0.5, '#000000') this.data.ctx.setLineCap('round'); this.data.ctx.setLineJoin('round'); this.data.ctx.stroke() this.data.ctx.draw(true) }, 5.touchend()事件清空数组,以便下一次的绘制 catchtouchend(e) { this.data.points = [] }, 3.代码测试优化

        将代码运行在真机时,发现会随着绘制的轨迹越长开始产生卡顿:

因为每次绘制时,绘制的都是所有轨迹点,这样轨迹点越多消耗的性能越大。于是换一个思路:每两个点连成一条线段 touchstart() 事件触发时作为第一条线段的起点, touchmove()事件触发时为第一条线段的终点,绘制出线段后再将这个点设为下一条线段的起点依次绘制。这样就很明显的解决了卡顿问题。

catchtouchstart(e) { if (this.data.drawCount == 0) { this.data.ctx.draw(false) } this.data.drawCount++ this.data.ctx.moveTo(e.changedTouches[0].clientX, e.changedTouches[0].clientY) }, catchtouchmove(e) { if (this.data.drawState == "stop") return this.data.drawState = "ing" if (e.touches.length > 1) { return } this.data.ctx.setStrokeStyle('#000000'); this.data.ctx.setLineWidth(3); this.data.ctx.setShadow(0, 0, 0.5, '#000000') this.data.ctx.setLineCap('round'); this.data.ctx.setLineJoin('round'); this.data.ctx.lineTo(e.changedTouches[0].clientX, e.changedTouches[0].clientY) this.data.ctx.stroke() this.data.ctx.draw(true) this.data.ctx.moveTo(e.changedTouches[0].clientX, e.changedTouches[0].clientY) },

修改后的效果:

结尾

        在真机测试时发现还会有多点触控的情况发生,想到的解决方案是多点触控时判断changedTouches[0]的坐标与上一次绘制的坐标点作比较,小于某个值时则认为是同一个手指触发的行为。但是方案被产品否了,场景出现的几率较低,只需要判断单点触控时才绘制就可以。

文末附上完整代码:

重新绘制 确认 Page({ data: { ctx: null, width: null, height: null, drawCount: 0, drawState: "init" }, onShow: function () { this.initCanvas() }, initCanvas() { let { width, height } = this.data width = wx.getSystemInfoSync().windowWidth height = wx.getSystemInfoSync().windowHeight console.log(wx.getSystemInfoSync()) this.data.ctx = wx.createCanvasContext('canvas') this.setData({ width, height }) this.clearCanvas() }, clearCanvas() { this.data.drawCount = 0 this.data.drawState = "ing" this.data.ctx.setTextBaseline('top') this.data.ctx.setTextAlign('center') this.data.ctx.setFontSize(20) this.data.ctx.setFillStyle('#616165'); this.data.ctx.fillText("请灰色区域内完成签名", this.data.width / 2, 30) this.data.ctx.draw(false) }, catchtouchstart(e) { if (this.data.drawCount == 0) { this.data.ctx.draw(false) } this.data.drawCount++ this.data.ctx.moveTo(e.changedTouches[0].clientX, e.changedTouches[0].clientY) }, catchtouchmove(e) { if (this.data.drawState == "stop") return this.data.drawState = "ing" if (e.touches.length > 1) { return } this.data.ctx.setStrokeStyle('#000000'); this.data.ctx.setLineWidth(3); this.data.ctx.setShadow(0, 0, 0.5, '#000000') this.data.ctx.setLineCap('round'); this.data.ctx.setLineJoin('round'); this.data.ctx.lineTo(e.changedTouches[0].clientX, e.changedTouches[0].clientY) this.data.ctx.stroke() this.data.ctx.draw(true) this.data.ctx.moveTo(e.changedTouches[0].clientX, e.changedTouches[0].clientY) }, canvasToImg() { if (this.data.drawState == "init") return this.data.drawState = "stop" wx.canvasToTempFilePath({ canvasId: 'canvas', success: res => { console.log(res.tempFilePath) // ... // 上传服务器 wx.navigateTo({ url: '/pages/showImg/showImg?src=' + res.tempFilePath, }) } }) } }) page{ position: relative; background-color: #f2f2f2; width: 100%; height: 100%; } canvas{ width: 100%; height: 100vh; } .btn-reset{ width: 100rpx; position: absolute; bottom: 10rpx; right: 160rpx; padding: 8rpx; text-align: center; border: 1rpx solid #4965B3; color: #4965B3; font-size: 18rpx; border-radius: 8rpx; box-sizing: border-box; } .btn-ok{ width: 100rpx; position: absolute; bottom: 10rpx; right: 30rpx; padding: 8rpx; text-align: center; background-color: #4965B3; border: 1rpx solid #4965B3; color: #fff; font-size: 18rpx; border-radius: 8rpx; box-sizing: border-box; }


【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3