前言
最近公司要在小程序上实现签字笔迹追踪,看了下网上关于Taro如何实现的文章,代码都很乱,很杂,所以,想记录下自己是如何实现的,并附上源码。
效果演示
源码
index.js
import Taro, { Component } from '@tarojs/taro'; import { View, Canvas } from '@tarojs/components'; import './index.less'; // 图片url(或filepath)转base64 const fileTobase64 = path => { return 'data:image/jpeg;base64,' + Taro.getFileSystemManager().readFileSync(path, 'base64'); }; // 签字轨迹 const historyTrajectory = { historyData:[ { //操作时间 time: 0, /** * 操作类型 * 绘图:mapping */ operation: 'mapping',//操作类型 /** * 绘制路径 * startX:开始x坐标 * startY:开y纵坐标 * currentX:目标位置的 x 坐标 * currentY:目标位置的 y 坐标 * z:1代表画线时鼠标处于move状态,0代表处于松开状态 * colorStr:线的填充颜色 * pen:笔迹大小 */ lineArr: { startX: 0, startY: 0, currentX: 0, currentY: 0, z: 0, colorStr: '#000', pen: 3 } } ] }; export default class Signature extends Component { constructor(props) { super(props); this.state = { }; this.pen = 3; this.context = ''; this.signPath = ''; this.moveToX = 0; this.moveToY = 0; // 签字开始时间 this.startDate = new Date().getTime(); this.timer = ''; this.colorStr = '#000'; } componentDidMount() { const context = Taro.createCanvasContext('myCanvas', this.$scope); this.context = context; this.context.draw(); this.context.lineWidth = this.pen; } // 手指触摸动作开始 touchStart = (e) => { this.context.lineWidth = this.pen; this.context.moveTo(e.changedTouches[0].x, e.changedTouches[0].y); this.moveToX = Math.floor(e.changedTouches[0].x); this.moveToY = Math.floor(e.changedTouches[0].y); historyTrajectory.historyData.push({ time: new Date().getTime() - this.startDate, operation: 'mapping', lineArr: { startX: this.moveToX, startY: this.moveToY, currentX: Math.floor(e.changedTouches[0].x), currentY: Math.floor(e.changedTouches[0].y), z: 1, colorStr: this.colorStr, pen: this.pen } }); } // 手指触摸后移动 touchMove = (e) => { const lineToX = Math.floor(e.changedTouches[0].x); const lineToY = Math.floor(e.changedTouches[0].y); this.context.lineWidth = this.pen; this.context.lineTo(lineToX, lineToY); this.context.stroke(); this.context.draw(true); this.context.moveTo(lineToX, lineToY); historyTrajectory.historyData.push({ time: new Date().getTime() - this.startDate, operation: 'mapping', lineArr: { startX: this.moveToX, startY: this.moveToY, currentX: lineToX, currentY: lineToY, z: 1, colorStr: this.colorStr, pen: this.pen } }); this.moveToX = lineToX; this.moveToY = lineToY; } onTouchEnd = () => { } // 清空画布 clearCanvas = () => { this.signPath = ''; this.context.draw(); historyTrajectory.historyData = []; historyTrajectory.historyData.push({ time: 0, operation: 'mapping', lineArr: { startX: 0, startY: 0, currentX: 0, currentY: 0, z: 0, colorStr: this.colorStr, pen: this.pen } }); } replay = () => { this.signPath = ''; this.context.draw(); clearInterval(this.timer); const startDate = new Date().getTime(); let i = 0; let len = historyTrajectory.historyData.length; this.timer = setInterval(() => { const curTime = new Date().getTime() - startDate; if (curTime >= historyTrajectory.historyData[i].time) { switch (historyTrajectory.historyData[i].operation) { case 'mapping': this.context.setStrokeStyle(historyTrajectory.historyData[i].lineArr.colorStr); this.context.lineWidth = historyTrajectory.historyData[i].lineArr.pen; this.context.moveTo(historyTrajectory.historyData[i].lineArr.startX, historyTrajectory.historyData[i].lineArr.startY); this.context.lineTo(historyTrajectory.historyData[i].lineArr.currentX, historyTrajectory.historyData[i].lineArr.currentY); this.context.stroke(); this.context.draw(true); break; } i++; } if(i >= len) { clearInterval(this.timer); } }, 1); } // 获取签字base64 confirm() { Taro.canvasToTempFilePath({ x: 0, y: 0, canvasId: 'myCanvas', success: (res) => { console.log('cofirm:', fileTobase64(res.tempFilePath)); }, fail: (error) => { console.error('btnConfirm:', error); } }, this.$scope); } render() { return ( <View className="signature-container"> <View className="canvas-area" style={{ height: (Taro.getSystemInfoSync().screenHeight - 150 + 'px') }}> <Canvas canvasId="myCanvas" className="my-canvas" disableScroll="false" onTouchStart={this.touchStart} onTouchMove={this.touchMove} onTouchEnd={this.onTouchEnd} /> </View> <View className="canvas-btn"> <View className="btn" onClick={this.confirm}>确认</View> <View className="btn" onClick={this.clearCanvas}>清除</View> <View className="btn" onClick={this.replay}>回放</View> </View> </View> ); } }
index.less
.signature-container { padding: 24px; .canvas-area { overflow: hidden; width: 100%; border: 2px dashed #dcdcdc; } .my-canvas { width: 100%; height: 100%; } .canvas-btn { display: flex; justify-content: space-around; align-items: center; margin-top: 24px; .btn { display: flex; justify-content: center; align-items: center; font-size: 28px; border-radius: 5px; flex: 1; background-color: #359a64; color: #fff; height: 60px; margin: 0 24px; } } }