基于 Taro 实现签字,轨迹回放
发布于
# Taro
前言
最近公司要在小程序上实现签字笔迹追踪,看了下网上关于 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;
}
}
}