wxml文件:
<!-- 签名画布 -->
<canvas class="canvas" id="signature-board" disable-scroll="true" bindtouchstart="canvasStart"
bindtouchmove="canvasMove" bindtouchend="canvasEnd" touchcancel="canvasEnd" type="2d">
</canvas>
新版使用id获取canvas组件即可,旧版需要使用canvas-id。
wxss文件:
/* pages/signature/signature.wxss */
.container {
padding: 20rpx;
height: 100vh;
width: 100vw;
overflow: hidden;
display: flex;
align-items: center;
/* background: radial-gradient(#fff, #000); */
background-repeat: no-repeat;
background-size: cover;
}
.block {
height: calc(calc(100vw - 60rpx - 40rpx) / 2);
/* max-height: 100%; */
width: 100%;
border-radius: 5rpx;
overflow: hidden;
display: flex;
align-items: center;
background: rgba(255,255,255,.5);
box-shadow: 0 0 30rpx 0 rgba(0,0,0,.3);
}
.drawarea {
flex-grow: 2;
height: 100%;
width: 100%;
position: relative;
}
.sign-hint {
position: absolute;
top: 0;
left: 10rpx;
right: 0;
bottom: 0;
z-index: 0;
margin: auto;
display: flex;
align-items: flex-end;
justify-content: flex-start;
font-size: 40rpx;
letter-spacing: .2em;
color: rgba(0,0,0,.2);
word-break: keep-all;
white-space: pre-line;
}
.canvas {
flex-grow: 2;
width: 100%;
height: 100%;
box-sizing: border-box;
}
.button-box {
flex-grow: 0;
flex-shrink: 0;
height: 100%;
width: 40rpx;
padding: 30rpx 10rpx 30rpx 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
}
.cu-btn {
width: 100%!important;
height: 50%!important;
padding: 0 2.5rpx 0 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 15rpx;
}
.cu-btn:first-of-type {
margin: 0 0 30rpx 0;
}
.btn-inner {
display: flex;
align-items: center;
justify-content: center;
writing-mode: vertical-rl;
color: #fff;
font-size: 14px;
}
json文件:
{
"usingComponents": {},
"pageOrientation": "landscape"
}
js文件:
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; // 包含上一点笔画信息的对象
Page({
data: {
drawn: false,
},
onShow: function (options) {
this.canvasInit();
},
canvasInit: function () {
wx.createSelectorQuery()
.select('#signature-board') // 在 WXML 中填入的 id
.fields({ node: true, size: true })
.exec((res) => {
res[0].node.width = res[0].width;
res[0].node.height = res[0].height;
context = res[0].node.getContext("2d")
})
},
canvasMove: function (e) {
this.setData({
drawn: true
})
let currPoint = {
x: e.changedTouches[0].x, // X坐标
y: e.changedTouches[0].y, // Y坐标
t: new Date().getTime(), // 当前时间
w: (MAX_LINE_WIDTH + MIN_LINE_WIDTH) / 2 /*默认宽度 */
};
if (lastPoint) {
currPoint.w = this.calcLineWidth(currPoint); // 重新赋值宽度,覆盖默认值
context.beginPath();
context.strokeStyle = '#000';
context.lineCap = 'round';
context.lineJoin = 'round';
context.lineWidth = currPoint.w;
context.moveTo(lastPoint.x, lastPoint.y);
context.lineTo(currPoint.x, currPoint.y);
context.stroke();
}
lastPoint = currPoint; // 结束前保存当前点为上一点
},
// 计算当前点的宽度,书写速度越快,笔画宽度越小,呈现出笔锋的感觉(笑)
calcLineWidth: function (currPoint) {
let consuming = currPoint.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(currPoint.x - lastPoint.x, 2) + Math.pow(currPoint.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;
},
canvasEnd: function (e) {
lastPoint = null; // 每笔画完清除缓存
},
canvasClear: function () {
this.setData({
drawn: false
})
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
// context.draw(false);
},
canvasOut: function () {
wx.navigateBack({
delta: 1,
})
},
finish: function () {
if (!this.data.drawn) {
return;
}
//由于新版的canvas的wx.canvasToTempFilePath方法一直报错,只能通过以下方式来获取签名图片
const res = context.canvas.toDataURL("image/png");
const fsm = wx.getFileSystemManager();
const FILE_BASE_NAME = "tmp_base64src_" + new Date().getTime();
const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME}.png`;
fsm.writeFile({
filePath,
data: res.replace(/^data:image\/\w+;base64,/, ""),
encoding: "base64",
success: () => {
this.getOpenerEventChannel().emit('signature', filePath); //传签名图片的临时路径回上一页
wx.navigateBack();
},
fail() {
wx.showToast({ title: "生成签名失败", icon: "none" });
},
});
},
})
签名效果: