网站如何做二维码,iapp影视源码,企业网站的优点和缺点,北仑宁波有没有做网站一、先看效果 小程序canvas电子签名 二、文档
微信小程序canvas 组件文档 微信小程序canvas API文档 H5Canvas文档
三、分析
1、初始话Canvas容器 2、Canvas触摸事件#xff0c;bindtouchstart#xff08;手指触摸动作开始#xff09;、bindtouchmove#xff08;手指触摸…一、先看效果 小程序canvas电子签名 二、文档
微信小程序canvas 组件文档 微信小程序canvas API文档 H5Canvas文档
三、分析
1、初始话Canvas容器 2、Canvas触摸事件bindtouchstart手指触摸动作开始、bindtouchmove手指触摸后移动、bindtouchend手指触摸动作结束、bindtouchcancel手指触摸动作被打断如来电提醒弹窗 3、记录每次从开始到结束的路径段 4、清除、撤销
四、代码分析
1、页面的布局、Canvas容器的初始化 1、先将屏幕横过来index.json配置文件“pageOrientation”: “landscape” 2、wx.getSystemInfoSync() 获取可使用窗口的宽高赋值给Canvas画布注意若存在按钮区域、屏幕安全区之类的需要减去 // 获取可使用窗口的宽高赋值给Canvas宽高要减去上下左右padding的20以及高度要减去footer区域wx.createSelectorQuery().select(.footer) // canvas获取节点.fields({node: true, size: true}) // 获取节点的相关信息node是否返回节点对应的 Node 实例size是否返回节点尺寸.exec((res) {// 获取手机左侧安全区域(刘海)const deviceInFo wx.getSystemInfoSync()const canvasWidth deviceInFo.windowWidth - 20 - deviceInFo?.safeArea?.left || 0const canvasHeight deviceInFo.windowHeight - res[0].height - 20console.log(canvasWidth, canvasWidth);console.log(canvasHeight, canvasHeight);this.setData({deviceInFo,canvasWidth,canvasHeight})this.initCanvas(init)})3、通过wx.createSelectorQuery()获取到canvas节点随即可获取到canvas的上下文实例 // 初始话Canvas画布initCanvas() {let ctx nulllet canvas null// 获取Canvas画布以及渲染上下文wx.createSelectorQuery().select(#myCanvas) // canvas获取节点.fields({node: true, size: true}) // 获取节点的相关信息node是否返回节点对应的 Node 实例size是否返回节点尺寸.exec((res) { // 执行所有的请求。请求结果按请求次序构成数组// Canvas 对象实例canvas res[0].node// Canvas 对象上下文实例动画动作绘图等都是在他的身上完成ctx canvas.getContext(2d)// Canvas 画布的实际绘制宽高const width res[0].width;const height res[0].height;// 获取设备像素比const dpr wx.getWindowInfo().pixelRatio;// 初始化画布大小canvas.width width * dpr;canvas.height height * dpr;// 画笔的颜色ctx.fillStyle rgb(200, 0, 0);// 指定了画笔绘制线条操作的线条宽度ctx.lineWidth 5// 缩小/放大图像ctx.scale(dpr, dpr)this.setData({canvas, ctx});})},2、线条的绘制 通过canva组件的触摸事件bindtouchstart、bindtouchmove、bindtouchend、bindtouchcancel结合canvas的路径绘制的方法moveTo(x,y)、lineTo(x,y)、stroke()来实现一段线条的绘制 1、bindtouchstart手指触摸动作开始结合moveTo(x,y) 用来设置绘图起始坐标的方法确定线段的开始坐标 // 手指触摸动作开始bindtouchstart(event) {let {type, changedTouches} event;let {x, y} changedTouches[0];ctx.moveTo(x, y); // 设置绘图起始坐标。},2、bindtouchend手指触摸动作结束结合lineTo(x,y) 来绘制一条直线最后stroke()渲染路径 // 手指触摸动作结束bindtouchend(event) {let {type, changedTouches} event;let {x, y} changedTouches[0];ctx.lineTo(x, y);// 绘制ctx.stroke();},3、但这只是一条直线段并未实现签名所需的曲线曲线实质上也是由无数个非常短小的直线段构成 4、bindtouchmove事件会在手指触摸后移动时实时返回当前状态 5、那么可否通过bindtouchmove 结合 moveTo lineTo stroke moveTo … 以上一次的结束为下一次的开始这样的方式来实时渲染直线段合并为一个近似的曲线 // 手指触摸后移动 bindtouchmove(event) {let {type, changedTouches} event;let {x, y} changedTouches[0];// 上一段终点ctx.lineTo(x, y) // 从最后一点到点(x,y)绘制一条直线。// 绘制ctx.stroke();// 下一段起点ctx.moveTo(x, y) // 设置绘图起始坐标。},6、归纳封装 // 手指触摸动作开始bindtouchstart(event) {this.addPathDrop(event)},// 手指触摸后移动 bindtouchmove(event) {this.addPathDrop(event)},// 手指触摸动作结束bindtouchend(event) {this.addPathDrop(event)},// 手指触摸动作被打断如来电提醒弹窗bindtouchcancel(event) {this.addPathDrop(event)},// 添加路径点addPathDrop(event) {let {ctx, historyImag, canvas} this.datalet {type, changedTouches} eventlet {x, y} changedTouches[0]if(type touchstart) { // 每次开始都是一次新动作// 最开始点ctx.moveTo(x, y) // 设置绘图起始坐标。} else {// 上一段终点ctx.lineTo(x, y) // 从最后一点到点(x,y)绘制一条直线。// 绘制ctx.stroke();// 下一段起点ctx.moveTo(x, y) // 设置绘图起始坐标。}},3、上一步、重绘、提交 主体思路为每一次绘制完成后都通过wx.canvasToTempFilePath生成图片并记录下来通过canvas的drawImage方法将图片绘制到 canvas 上 五、完整代码 1、inde.json {navigationBarTitleText: 电子签名,backgroundTextStyle: dark,pageOrientation: landscape,disableScroll: true,usingComponents: {van-button: vant/weapp/button/index,van-toast: vant/weapp/toast/index}
}2、index.wxml !-- index.wxml --
viewview classcontent stylepadding-left: {{deviceInFo.safeArea.left || 10}}pxview classcanvas_box!-- 定位到canvas画布的下方作为背景 --view classcanvas_tips签字区/view!-- canvas画布 --canvas classcanvas_content type2d stylewidth:{{canvasWidth}}px; height:{{canvasHeight}}px idmyCanvas bindtouchstartbindtouchstart bindtouchmovebindtouchmove bindtouchendbindtouchend bindtouchcancelbindtouchcancel/canvas/view/view!-- footer --view classfooter stylepadding-left: {{deviceInFo.safeArea.left}}pxvan-button plain classitem block iconreplay bind:clickoverwrite typewarning清除重写/van-buttonvan-button plain classitem block iconrevoke bind:clickprev typedanger撤销/van-buttonvan-button classitem block iconpassed bind:clickconfirm typeinfo提交/van-button/view
/view
!-- 提示框组件 --
van-toast idvan-toast / 2、index.less .content {box-sizing: border-box;width: 100%;height: 100%;padding: 10px;.canvas_box {width: 100%;height: 100%;background-color: #E8E9EC;position: relative;// 定位到canvas画布的下方作为背景.canvas_tips {position: absolute;left: 0;top: 0;width: 100%;height: 100%;font-size: 80px;color: #E2E2E2;font-weight: bold;display: flex;align-items: center;justify-content: center;}// .canvas_content {// width: 100%;// height: 100%;// }}
}
// 底部按钮
.footer {box-sizing: border-box;padding: 20rpx 0;z-index: 2;background-color: #ffffff;text-align: center;position: fixed;width: 100%;box-shadow: 0 0 15rpx rgba(0, 0, 0, 0.1);left: 0;bottom: 0;display: flex;.item {flex: 1;margin: 0 10rpx;}.scan {width: 80rpx;margin: 0 10rpx;}.moreBtn {width: 150rpx}
}3、index.js // index.js
// 获取应用实例
// import request from ../../request/index;
import Toast from vant/weapp/toast/toast;const app getApp()
Page({data: {// expertId: , // 专家iddeviceInFo: {}, // 设备信息canvasWidth: , // 画布宽canvasHeight: , // 画布高canvas: null, // Canvas 对象实例ctx: null, // Canvas 对象上下文实例historyImag: [], // 历史记录每一笔动作完成后的图片数据用于每一次回退上一步是当作图片绘制到画布上fileList: [], // 签名后生成的附件initialCanvasImg: , // 初始画布图,解决非ios设备重设置宽高不能清空画布的问题},onReady() {// 获取可使用窗口的宽高赋值给Canvas宽高要减去上下左右padding的20以及高度要减去footer区域wx.createSelectorQuery().select(.footer) // canvas获取节点.fields({ node: true, size: true }) // 获取节点的相关信息node是否返回节点对应的 Node 实例size是否返回节点尺寸.exec((res) {console.log(res, res);// 获取手机左侧安全区域(刘海)const deviceInFo wx.getSystemInfoSync()const canvasWidth deviceInFo.windowWidth - 20 - deviceInFo?.safeArea?.left || 0const canvasHeight deviceInFo.windowHeight - res[0].height - 20this.setData({deviceInFo,canvasWidth,canvasHeight})this.initCanvas(init)})},onLoad(option) {wx.setNavigationBarTitle({title: 电子签名})// const {expertId} option// this.setData({expertId})},// 初始话Canvas画布initCanvas(type) {let ctx nulllet canvas nulllet {historyImag, canvasWidth, canvasHeight, deviceInFo, initialCanvasImg} this.data// 获取Canvas画布以及渲染上下文wx.createSelectorQuery().select(#myCanvas) // canvas获取节点.fields({ node: true, size: true }) // 获取节点的相关信息node是否返回节点对应的 Node 实例size是否返回节点尺寸.exec((res) { // 执行所有的请求。请求结果按请求次序构成数组// Canvas 对象实例canvas res[0].node// Canvas 对象上下文实例动画动作绘图等都是在他的身上完成ctx canvas.getContext(2d)// Canvas 画布的实际绘制宽高const width res[0].widthconst height res[0].height// 获取设备像素比const dpr wx.getWindowInfo().pixelRatio// 初始化画布大小canvas.width width * dprcanvas.height height * dpr// 画笔的颜色ctx.fillStyle rgb(200, 0, 0);// 指定了画笔绘制线条操作的线条宽度ctx.lineWidth 5// 如果存在历史记录则将历史记录最新的一张图片拿出来进行绘制。非ios时直接加载一张初始的空白图片if(historyImag.length ! 0 || (deviceInFo.platform ! ios type ! init)) {// 图片对象const image canvas.createImage()// 图片加载完成回调image.onload () {// 将图片绘制到 canvas 上ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight)}// 设置图片srcimage.src historyImag[historyImag.length - 1] || initialCanvasImg;}// 缩小/放大图像ctx.scale(dpr, dpr)this.setData({canvas, ctx})// 保存一张初始空白图片if(type init) {wx.canvasToTempFilePath({canvas,png: png,success: res {// 生成的图片临时文件路径const tempFilePath res.tempFilePaththis.setData({initialCanvasImg: tempFilePath})},})}})},// 手指触摸动作开始bindtouchstart(event) {this.addPathDrop(event)},// 手指触摸后移动 bindtouchmove(event) {this.addPathDrop(event)},// 手指触摸动作结束bindtouchend(event) {this.addPathDrop(event)},// 手指触摸动作被打断如来电提醒弹窗bindtouchcancel(event) {this.addPathDrop(event)},// 添加路径点addPathDrop(event) {let {ctx, historyImag, canvas} this.datalet {type, changedTouches} eventlet {x, y} changedTouches[0]if(type touchstart) { // 每次开始都是一次新动作// 最开始点ctx.moveTo(x, y) // 设置绘图起始坐标。} else {// 上一段终点ctx.lineTo(x, y) // 从最后一点到点(x,y)绘制一条直线。// 绘制ctx.stroke();// 下一段起点ctx.moveTo(x, y) // 设置绘图起始坐标。}// 每一次结束或者意外中断保存一份图片到历史记录中if(type touchend || type touchcancel) {// 生成图片// historyImag.push(canvas.toDataURL(image/png))wx.canvasToTempFilePath({canvas,png: png,success: res {// 生成的图片临时文件路径const tempFilePath res.tempFilePathhistoryImag.push(tempFilePath)this.setData(historyImag)},})}},// 上一步prev() {this.setData({historyImag: this.data.historyImag.slice(0, this.data.historyImag.length - 1)})this.initCanvas()},// 重写overwrite() {this.setData({historyImag: []})this.initCanvas()},// 提交confirm() {const {canvas, historyImag} this.dataif(historyImag.length 0) {Toast.fail(请先签名后保存);return}// 生成图片wx.canvasToTempFilePath({canvas,png: png,success: res {// 生成的图片临时文件路径const tempFilePath res.tempFilePath// 保存图片到系统wx.saveImageToPhotosAlbum({filePath: tempFilePath,})// this.beforeRead(res.tempFilePath)},})},// // 图片上传// async beforeRead(tempFilePath) {// const that this;// wx.getImageInfo({// src: tempFilePath,// success(imageRes) {// wx.uploadFile({// url: , // 仅为示例非真实的接口地址// filePath: imageRes.path,// name: file,// header: {token: wx.getStorageSync(token)},// formData: {// ext: imageRes.type// },// success(fileRes) {// const response JSON.parse(fileRes.data);// if (response.code 200) {// that.setData({// fileList: [response.data]// })// that.submit();// } else {// wx.hideLoading();// Toast.fail(附件上传失败);// return false;// }// },// fail(err) {// wx.hideLoading();// Toast.fail(附件上传失败);// }// });// },// fail(err) {// wx.hideLoading();// Toast.fail(附件上传失败);// }// })// },// 提交// submit() {// const {fileList} this.data// wx.showLoading({title: 提交中...,})// request(post, , {// fileIds: fileList.map(item item.id),// }).then(res {// if (res.code 200) {// wx.hideLoading();// Toast.success(提交成功);// setTimeout(() {// wx.navigateBack({delta: 1});// }, 1000)// }// })// },
})