
Uniapp中实现APP手写签名功能
手写签名功能在移动应用中有着广泛的应用场景,如电子合同签署、审批确认、单据验证等。本文将介绍如何在Uniapp中实现APP端的手写签名功能。
一、实现思路
在Uniapp中实现手写签名主要有两种方式:
使用Canvas绘图API实现自定义手写板
使用第三方插件或原生模块
本文将重点介绍第一种方式,即使用Canvas实现,这种方式跨平台兼容性好,且不需要依赖第三方库。
二、基础实现步骤
- 创建签名画布
<template>
<view class="signature-container">
<canvas
canvas-id="signatureCanvas"
class="signature-canvas"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
></canvas>
<view class="button-group">
<button @click="clearSignature">清除</button>
<button @click="saveSignature">保存</button>
</view>
</view>
</template>
- 样式设置
<style>
.signature-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx;
}
.signature-canvas {
width: 100%;
height: 500rpx;
background-color: #f8f8f8;
border: 1rpx solid #ddd;
}
.button-group {
display: flex;
justify-content: space-around;
width: 100%;
margin-top: 40rpx;
}
button {
width: 45%;
}
</style>
- JavaScript逻辑实现
<script>
export default {
data() {
return {
points: [], // 存储触摸点
ctx: null, // canvas上下文
canvasWidth: 0,
canvasHeight: 0,
isDrawing: false
}
},
onReady() {
this.initCanvas()
},
methods: {
// 初始化画布
initCanvas() {
const query = uni.createSelectorQuery().in(this)
query.select('.signature-canvas')
.fields({ node: true, size: true })
.exec(res => {
const canvas = res[0].node
this.ctx = canvas.getContext('2d')
this.canvasWidth = res[0].width
this.canvasHeight = res[0].height
// 设置canvas实际渲染尺寸
canvas.width = res[0].width * uni.getSystemInfoSync().pixelRatio
canvas.height = res[0].height * uni.getSystemInfoSync().pixelRatio
// 缩放画布以适配高清屏
this.ctx.scale(
uni.getSystemInfoSync().pixelRatio,
uni.getSystemInfoSync().pixelRatio
)
this.ctx.lineWidth = 3
this.ctx.lineCap = 'round'
this.ctx.lineJoin = 'round'
this.ctx.strokeStyle = '#000000'
})
},
// 触摸开始
handleTouchStart(e) {
this.isDrawing = true
const point = {
x: e.touches[0].x,
y: e.touches[0].y
}
this.points.push(point)
this.ctx.beginPath()
this.ctx.moveTo(point.x, point.y)
},
// 触摸移动
handleTouchMove(e) {
if (!this.isDrawing) return
const point = {
x: e.touches[0].x,
y: e.touches[0].y
}
this.points.push(point)
this.ctx.lineTo(point.x, point.y)
this.ctx.stroke()
},
// 触摸结束
handleTouchEnd() {
this.isDrawing = false
this.points = []
},
// 清除签名
clearSignature() {
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
this.points = []
},
// 保存签名
saveSignature() {
uni.canvasToTempFilePath({
canvasId: 'signatureCanvas',
success: res => {
// 这里可以保存到本地或上传服务器
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
uni.showToast({
title: '签名保存成功',
icon: 'success'
})
},
fail: () => {
uni.showToast({
title: '保存失败',
icon: 'none'
})
}
})
},
fail: err => {
console.error(err)
uni.showToast({
title: '保存失败',
icon: 'none'
})
}
}, this)
}
}
}
</script>
三、功能优化
- 添加背景网格线
drawGrid() {
this.ctx.strokeStyle = '#e0e0e0'
this.ctx.lineWidth = 1
// 画横线
for (let i = 0; i < this.canvasHeight; i += 20) {
this.ctx.beginPath()
this.ctx.moveTo(0, i)
this.ctx.lineTo(this.canvasWidth, i)
this.ctx.stroke()
}
// 画竖线
for (let i = 0; i < this.canvasWidth; i += 20) {
this.ctx.beginPath()
this.ctx.moveTo(i, 0)
this.ctx.lineTo(i, this.canvasHeight)
this.ctx.stroke()
}
this.ctx.strokeStyle = '#000000'
this.ctx.lineWidth = 3
}
- 添加撤销功能
// 在data中添加
history: [],
// 修改handleTouchEnd方法
handleTouchEnd() {
if (this.points.length > 0) {
this.history.push(this.ctx.getImageData(0, 0, this.canvasWidth, this.canvasHeight))
}
this.isDrawing = false
this.points = []
},
// 添加撤销方法
undo() {
if (this.history.length > 0) {
this.ctx.putImageData(this.history.pop(), 0, 0)
}
}
- 支持不同颜色和粗细
// 在data中添加
penOptions: {
colors: ['#000000', '#ff0000', '#00ff00', '#0000ff'],
sizes: [1, 3, 5, 8],
selectedColor: '#000000',
selectedSize: 3
},
// 修改绘图方法
handleTouchStart(e) {
// ...
this.ctx.lineWidth = this.penOptions.selectedSize
this.ctx.strokeStyle = this.penOptions.selectedColor
// ...
}
四、注意事项
Canvas尺寸问题:在移动设备上,Canvas的实际渲染尺寸和逻辑尺寸可能不同,需要根据设备像素比进行适配。
性能优化:对于复杂的签名,可以适当减少记录的点数或使用贝塞尔曲线优化。
跨平台兼容性:虽然Uniapp是跨平台的,但不同平台对Canvas的支持可能有细微差异,需要充分测试。
高清屏适配:在高DPI设备上,需要特别注意Canvas的清晰度问题。
保存格式:根据需求选择合适的图片格式(PNG/JPG),PNG支持透明背景,JPG文件更小。
五、完整组件封装
可以将签名功能封装为可复用的组件:
<!-- components/signature-pad/signature-pad.vue -->
<template>
<view class="signature-pad">
<canvas
canvas-id="signatureCanvas"
class="signature-canvas"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
></canvas>
<view class="controls">
<button @click="clear">清除</button>
<button @click="undo">撤销</button>
<button @click="save">保存</button>
</view>
<view class="pen-options" v-if="showPenOptions">
<view class="color-options">
<view
v-for="color in penOptions.colors"
:key="color"
class="color-option"
:style="{backgroundColor: color}"
@click="selectColor(color)"
></view>
</view>
<view class="size-options">
<view
v-for="size in penOptions.sizes"
:key="size"
class="size-option"
@click="selectSize(size)"
>
<view
class="size-dot"
:style="{
width: size + 'px',
height: size + 'px',
backgroundColor: penOptions.selectedColor
}"
></view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'SignaturePad',
props: {
showPenOptions: {
type: Boolean,
default: true
}
},
data() {
return {
// ...之前的data内容
}
},
methods: {
// ...之前的方法
selectColor(color) {
this.penOptions.selectedColor = color
},
selectSize(size) {
this.penOptions.selectedSize = size
},
getSignature() {
return new Promise((resolve, reject) => {
uni.canvasToTempFilePath({
canvasId: 'signatureCanvas',
success: res => resolve(res.tempFilePath),
fail: err => reject(err)
}, this)
})
}
}
}
</script>
六、使用第三方插件
如果需要更强大的功能,可以考虑使用第三方插件:
uniapp-signature:专为Uniapp开发的签名插件
html2canvas:通过webview方式实现的截图方案
原生插件:对于性能要求高的场景,可以考虑开发原生插件
七、总结
本文介绍了在Uniapp中实现手写签名功能的完整方案,从基础的Canvas绘图到功能优化和组件封装。这种实现方式跨平台兼容性好,不需要依赖第三方库,适合大多数应用场景。对于更复杂的需求,可以考虑使用第三方插件或开发原生模块。
实际开发中,还需要根据具体业务需求进行调整,如添加时间戳、用户信息水印、多页签名等功能。
