canvas实现签名功能
作为一个开发小白,在查找了网上的一堆资料后最终决定使用canvas来实现签名功能,点击签名按钮后弹出弹框签名,签名后点击保存成图片转成base64后上传到后端,现在将将相关经验分享出来,如有问题请斧正相互学习,谢谢~
页面代码
<view class="title-level2"><text class="text-bold2">负责人签名:</text></view>
<view class="weui-cell__bd" style="padding-bottom: 150px;">
<view class="state_line signed">
<image class="temp_img" wx:if="{{concatSignUrl.length > 0}}" src="{{concatSignUrl}}" />
<button bindtap="onPopupOpen" class="clean-button" wx:if="{{concatSignUrl.length > 0}}" bindtap="onPopupOpen">重置签名</button>
<!-- <view class="ma_r20 colorBlue" wx:else bindtap="onPopupOpen">{{ lineTitle }}</view> -->
<button class="centered-button" wx:else bindtap="onPopupOpen">签名</button>
<!-- 签名 start -->
<van-popup custom-class="signed_popup" lock-scroll="{{ true }}" show="{{ showPopupc }}" closeable="true" catchtouchmove="true" bind:close="onPopupClose">
<view class="top_view">签名区域</view>
<view class="cont_view">
<canvas class="canvastyle" type="2d" id='canvasId' bindtouchstart="bindtouchstart" bindtouchmove="bindtouchmove">
</canvas>
</view>
<view class="foot_view">
<text class="reset" bindtap="clear">重置</text>
<text class="confirm" bindtap="export">确定</text>
</view>
</van-popup>
<!-- 签名 end -->
</view>
</view>
点击签名,弹出弹窗,同时在每次打开时初始化画板
onPopupOpen() {
var that = this
that.setData({
showPopupc: true,
})
setTimeout(function () {
that.initialize();
that.delayedClear();
}, 200)
},
初始化画板
initialize() {
wx.createSelectorQuery()
.select('#canvasId')
.fields({
node: true,
size: true
})
.exec((res) => {
// Canvas 对象
const canvas = res[0].node
// 渲染上下文
const ctx = canvas.getContext('2d')
// Canvas 画布的实际绘制宽高
const width = res[0].width
const height = res[0].height
// 初始化画布大小
const windowInfo = wx.getSystemInfoSync()
const dpr = windowInfo.pixelRatio
const dprW = width * dpr
const dprH = height * dpr
canvas.width = dprW
canvas.height = dprH
ctx.scale(dpr, dpr)
// 设置线条宽度
ctx.lineWidth = 3; // 这里可以调整线条的宽度,单位是像素
this.setData({
canvas: canvas,
ctxContext: ctx,
ctxWidth: width,
ctxHeight: height,
ctxDpr: dpr
})
})
},
签名画线
bindtouchstart(e) {
this.data.ctxContext.moveTo(e.changedTouches[0].x, e.changedTouches[0].y)
},
bindtouchmove(e) {
this.setData({
flagMove: true
})
this.data.ctxContext.lineTo(e.changedTouches[0].x, e.changedTouches[0].y);
this.data.ctxContext.stroke();
this.data.ctxContext.moveTo(e.changedTouches[0].x, e.changedTouches[0].y);
},
真机签名实测
清除画板代码
delayedClear() {
var that = this;
setTimeout(function () {
that.clear(); // Clear the canvas after a short delay
}, 50);
},
clear() {
var w = this.data.ctxWidth
var h = this.data.ctxHeight
this.data.ctxContext.clearRect(0, 0, w, h)
this.data.ctxContext.beginPath()
this.setData({
flagMove: false
})
},
将图片上传到后端保存转成base64
export () {
const that = this;
if (!that.data.flagMove) {
wx.showToast({
duration: 3000,
title: '请您先签名',
icon: 'none'
})
return;
}
// 生成图片
wx.canvasToTempFilePath({
canvas: that.data.canvas,
success: res => {
const tempFilePath = res.tempFilePath
that.setData({
concatSignUrl: tempFilePath,
showPopupc: false
})
// 将图片转为 Base64
that.imageToBase64(tempFilePath, function (error, base64String) {
if (error) {
console.error('Base64 转换失败', error);
return;
}
// 上传 Base64 编码的图片
that.uploadImg(base64String);
});
},
})
},
imageToBase64(tempFilePath, callback) {
wx.getFileSystemManager().readFile({
filePath: tempFilePath,
encoding: 'base64',
success: function (res) {
// 拼接与 PC 端一致的格式
var base64Str = "data:image/svg+xml;base64," + res.data;
callback(null, base64Str);
},
fail: function (error) {
callback(error);
}
});
},
uploadImg(base64) {
var _this = this;
const params = {
base64: base64,
};// 对象传参
request.POST('miniapp/miniappUploadBase64', params, function (res) {
if (res.data && res.data.code === 0) {
_this.setData({
signFile: res.data.fileName,
});
} else {
wx.hideToast();
//app.showErrorModal(res.data.msg, '修改失败');
}
}, function (res) {
wx.hideToast();
//app.showErrorModal(res.msg, '修改失败');
});
},
部分样式代码
.centered-button{
background-color: #1ab394;
color: #fff;
margin-top: 20px;
margin-left: auto;
margin-right: auto;
margin-bottom: auto;
border-radius: 20px;
}
.clean-button{
background-color: rgb(241, 80, 80);
margin-top: 10px;
margin-left: auto;
margin-right: auto;
margin-bottom: auto;
color: white;
border-radius: 20px;
}
/* 签名样式 */
.signed .temp_img {
cursor: pointer;
width: 98%;
height: 200rpx;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid rgb(107, 107, 107); /* 添加边框 */
margin: auto; /* 水平居中 */
margin-top: 10px; /* 调整图片靠下的距离,可以根据需要调整值 */
border-radius: 10px; /* 设置边框为圆角,可以根据需要调整半径 */
}
.signed .signed_popup {
display: flex;
flex-direction: column;
position: fixed;
width: 100%;
height: 500rpx;
border-radius: 50rpx;
}
.signed_popup .top_view {
text-align: center;
height: 100rpx;
line-height: 100rpx;
font-size: 32rpx;
font-weight: 500;
}
.signed_popup .fullImg {
position: absolute;
top: 30rpx;
right: 40rpx;
width: 40rpx;
height: 40rpx;
}
.signed_popup .cont_view {
flex: 1;
margin: 0 40rpx 30rpx;
}
.signed_popup .canvastyle {
width: 100%;
height: 100%;
background: #F7F8FA;
}
.signed_popup .foot_view {
display: flex;
height: 100rpx;
border-top: 1rpx solid #EBEDF0;
}
.signed_popup .reset {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
font-size: 38rpx;
color: #323233;
border-right: 1rpx solid #EBEDF0;
}
.signed_popup .confirm {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
font-size: 38rpx;
color: #1483FE;
}
出现的问题
在微信开发者工具中测试画板时,会出现偏移的问题,但是在真机上面就没有,可能是微信接口问题,还有就在微信开发者工具中可以正常使用签名画板,但是在手机上面没有出现可以使用微信开发者工具中的详情,里面的调试基础库,推送到自己的手机上面,再次运行就可以正常签名。其次也可以发布到体验版上面测试,也是可以正常签名的