前言
目前做的一个项目需要用到电子签名的功能,网上其实也挺多这种类型的电子签名,但是带有笔锋效果的确比较少,所以参考了一些博客,总结成了这个功能,在此分享给大家。
效果展示
代码展示
触摸开始(touchstart)
touchstart(e) {
if (!this.isInit) {
this.isInit = true
this.autographClick(1);
}
let startX = e.changedTouches[0].x
let startY = e.changedTouches[0].y
let startPoint = {
X: startX,
Y: startY
}
this.points.push(startPoint)
//每次触摸开始,开启新的路径
// console.log(this.points, "point11")
this.canvasCtx.beginPath()
}
获取最开始的坐标,x和y,然后将他们存起来,注意每次触摸后都应该调用 beginPath()
。
触摸移动(touchmove)
touchmove(e) {
let moveX = e.changedTouches[0].x
let moveY = e.changedTouches[0].y
let movePoint = {
X: moveX,
Y: moveY,
T: new Date().getTime(),
W: (MAX_LINE_WIDTH + MIN_LINE_WIDTH) / 2
}
this.points.push(movePoint) //存点
if (lastPoint) {
// console.log(lastPoint.T, movePoint.T)
movePoint.W = this.calcLineWidth(movePoint); // 重新赋值宽度,覆盖默认值
this.canvasCtx.beginPath();
this.canvasCtx.strokeStyle = '#000';
this.canvasCtx.lineCap = 'round';
this.canvasCtx.lineJoin = 'round';
this.canvasCtx.lineWidth = movePoint.W;
this.canvasCtx.moveTo(lastPoint.X, lastPoint.Y);
this.canvasCtx.lineTo(movePoint.X, movePoint.Y);
this.canvasCtx.stroke();
}
lastPoint = movePoint; // 结束前保存当前点为上一点
let len = this.points.length
if (len >= 2) {
this.draw() //绘制路径
}
}
获取移动的坐标,将坐标、时间和画笔宽度进行存点。
笔锋效果(calcLineWidth)
const MAX_V = 1; // 最大书写速度
const MIN_V = 0; // 最小书写速度
const MAX_LINE_WIDTH = 16; // 最大笔画宽度
const MIN_LINE_WIDTH = 4; // 最小笔画宽度
const MAX_LINE_DIFF = .03; // 两点之间笔画宽度最大差异
let context = null; // canvas上下文
let lastPoint = null; // 包含上一点笔画信息的对象
calcLineWidth(movePoint) {
let consuming = movePoint.T - lastPoint.T; // 两点之间耗时
if (!consuming) return lastPoint.W; // 如果当前点用时为0,返回上点的宽度。
// 当前点的最大宽度
let maxWidth = Math.min(MAX_LINE_WIDTH, lastPoint.W * (1 + MAX_LINE_DIFF));
// 当前点的最小宽度,变细时速度快所以宽度变化要稍快
let minWidth = Math.max(MIN_LINE_WIDTH, lastPoint.W * (1 - MAX_LINE_DIFF * 3));
// 两点之间距离
let distance = Math.sqrt(Math.pow(movePoint.X - lastPoint.X, 2) + Math.pow(movePoint.Y - lastPoint.Y, 2));
/*当前点速度*/
let speed = Math.max(Math.min(distance / consuming, MAX_V), MIN_V);
/* 当前点宽度 */
let lineWidth = Math.max(Math.min(MAX_LINE_WIDTH * (1 - speed / MAX_V), maxWidth), minWidth);
return lineWidth;
}
在绘制的过程,通过两点之间的距离、速度计算出宽度,再进行绘制,也就是笔锋的效果。可以自己调节初始值,设置成自己最想要的效果。
绘制笔迹(draw)
draw() {
let point1 = this.points[0]
let point2 = this.points[1]
this.points.shift()
this.canvasCtx.moveTo(point1.X, point1.Y)
this.canvasCtx.lineTo(point2.X, point2.Y)
this.canvasCtx.stroke()
this.canvasCtx.draw(true)
this.hasSign = true
}
- 为保证笔迹实时显示,必须在移动的同时绘制笔迹;
- 为保证笔迹连续,每次从路径集合中区两个点作为起点(moveTo)和终点(lineTo);
- 将上一次的终点作为下一次绘制的起点(即清除第一个点)。
触摸结束(touchend)
touchend() {
this.points = []
this.canvasCtx.draw(true)
lastPoint = null;
}
最后别忘记再每次结束时,要进行清空。
总结
总体来说,虽然达到了效果,但是可能运行不是特别流畅,没有达到商业级的效果,还有待加强,大家可以提出自己的见解,我会参考并完善。
代码地址:https://gitee.com/Robergean/Signature