前言:最近接到了这样一个需求,需要在pc端和小程序端实现手写签名,两者都可以基于canvas实现手写签名功能
一、PC端手写签名
javascript
// 此功能是基于Vue2实现的
<el-form-item label="签字" prop="auditSignImg">
<div style="position: relative; width: 300px; height: 200px;">
<canvas id="mycanvas" @mousedown="mousedown"></canvas>
<i class="el-icon-refresh resetsign" style="
font-size: 30px;
position: absolute;
right: 0;
bottom: 0;
" @click="clearcanvas" title="重置签名"></i>
</div>
</el-form-item>
mounted() {
this.$nextTick(() => this.createCanvas())
},
methods: {
// 关于签名
createCanvas() {
// 获取canvas 实例
var canvas = document.getElementById("mycanvas");
// 设置宽高
canvas.width = this.config.width;
canvas.height = this.config.height;
// 设置一个边框,方便咱们检查及运用
canvas.style.border = "1px solid #ccc";
// 创立上下文
this.ctx = canvas.getContext("2d");
// 设置填充背景色
this.ctx.fillStyle = "#fff";
// 制作填充矩形
this.ctx.fillRect(
0, // x 轴开端制作方位
0, // y 轴开端制作方位
this.config.width, // 宽度
this.config.height // 高度
);
},
mousedown(e) {
// 初始化
// 获取偏移量及坐标
const {
offsetX,
offsetY,
pageX,
pageY
} = e;
// 修正前次的偏移量及坐标
this.client.offsetX = offsetX;
this.client.offsetY = offsetY;
this.client.endX = pageX;
this.client.endY = pageY;
// 铲除以上一次 beginPath 之后的一切途径,进行制作
this.ctx.beginPath();
// 依据装备文件设置进行相应装备
this.ctx.lineWidth = this.config.lineWidth;
this.ctx.strokeStyle = this.config.strokeStyle;
this.ctx.lineCap = this.config.lineCap;
this.ctx.lineJoin = this.config.lineJoin;
// 设置画线开端点位
this.ctx.moveTo(this.client.offsetX, this.client.offsetY);
this.ctx.stroke();
window.addEventListener("mousemove", this.mousemove);
},
mousemove(e) {
// 获取当时坐标点位
const {
offsetX,
offsetY
} = e;
// 依据坐标点位移动增加线条
let h = document.getElementById("mycanvas").getBoundingClientRect().top;
let w = document.getElementById("mycanvas").getBoundingClientRect().left;
this.ctx.lineTo(e.clientX - w, e.clientY - h);
// 制作
this.ctx.stroke();
window.addEventListener("mouseup", this.mouseup);
},
mouseup() {
this.ctx.closePath();
// 移除鼠标移动或手势移动监听器
window.removeEventListener("mousemove", this.mousemove);
},
clearcanvas() {
// 清空画布
this.ctx.clearRect(0, 0, this.config.width, this.config.height);
// 清空签名url
this.rejectForm.auditSignImg = '';
},
}
二、小程序端手写签名
javascript
// 此功能是基于uniapp+Vue3实现的
// SignCanvas.vue
<!-- 签名 -->
<template>
<view class="canvas-signature">
<canvas id="myCanvas" canvas-id="myCanvas" :style="{width: cWidth + 'px', height: cHeight + 'px', boxShadow: '-2rpx 2rpx 2rpx 0 rgba(0,0,0,0.1), 2rpx -2rpx 2rpx 0 rgba(0,0,0,0.1)',
borderRadius: '8rpx 8rpx 8rpx 8rpx'}" @touchstart.stop="touchStart" @touchmove.stop="touchMove" @touchend.stop="touchEnd">
</canvas>
<view class="btn-wrap">
<view class="btn clean base-btn" @click="clear">
<text>重写</text>
</view>
<view class="btn save base-btn" @click="save">
<text>{{btnText}}</text>
</view>
</view>
</view>
</template>
<script>
import { uploadFile } from '@/utils/file.js'
import { $showToast } from '@/utils/index.js'
let ctx = null
export default {
props:{
cHeight: {
type: Number,
default: 200
},
strokeStyle: {
type: String,
default: '#000'
},
lineWidth: {
type: Number,
default: 3
},
btnText: {
type: String,
default: '保存'
},
pageType: {
type: String,
default: ''
}
},
data() {
return {
cWidth: 375,
touchStartX: 0, // 手指触摸开始x坐标
touchStartY: 0, // 手指触摸开始y坐标
moveX: 0, // 移动x坐标
moveY: 0, // 移动y坐标
hasContent: false // 是否已签名
}
},
mounted() {
const res = uni.getWindowInfo()
this.cWidth = res.screenWidth * 0.9
ctx = uni.createCanvasContext('myCanvas', this)
ctx.lineWidth = this.lineWidth
ctx.strokeStyle = this.strokeStyle
ctx.lineCap = 'round'
},
methods: {
touchStart(e) {
const {touches} = e
this.touchStartX = touches[0].x
this.touchStartY = touches[0].y
// 每次触摸开始,开启新的路径
ctx.beginPath()
},
touchMove(e) {
const {touches} = e
this.moveX = touches[0].x
this.moveY = touches[0].y
// 两点画一笔
ctx.moveTo(this.touchStartX, this.touchStartY)
ctx.lineTo(this.moveX,this.moveY)
ctx.stroke()
ctx.draw(true) // 将上一次的终点作为下一次绘制的起点
this.touchStartX = this.moveX
this.touchStartY = this.moveY
this.hasContent = true
},
touchEnd(e) {},
// 保存
save() {
if (!this.hasContent) {
$showToast('请先签名')
return
}
uni.canvasToTempFilePath({
canvasId: 'myCanvas',
success: (res) => {
uploadFile(res?.tempFilePath).then(result => {
this.$emit('handleSingature', result?.fileName)
})
},
fail: (error) => {
console.log('回调错误', error);
}
}, this)
},
// 重写
clear() {
this.hasContent = false
ctx.clearRect(0, 0, this.cWidth, this.cHeight)
ctx.draw(true)
}
}
}
</script>
<style lang="scss" scoped>
.canvas-signature {
background: #FFFFFF;
.btn-wrap {
display: flex;
justify-content: space-around;
background: #fff;
padding: 20rpx 0;
border-top: 1rpx solid #f5f5f5;
.btn,&.clean {
width: 40%;
height: 104rpx;
line-height: 104rpx;
border-radius: 20rpx 20rpx 20rpx 20rpx;
font-size: 36rpx;
}
.btn {
padding: 0 14rpx;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
background: #0FAD70;
&.clean {
margin-right: 20rpx;
background-color: #ffffff;
border: 2rpx solid #C6C6C8;
color: #333333;
}
}
}
}
</style>
// 父组件引用SignCanvas.vue
<my-canvas cHeight="460" @handleSingature="handleSingature"></my-canvas>
handleSingature(fileName) {
// 手写签名转成的文件名
console.log(fileName)
}