169 lines
3.4 KiB
Vue
169 lines
3.4 KiB
Vue
<script setup lang="ts">
|
||
import { getCurrentInstance, nextTick, ref, watch } from 'vue'
|
||
|
||
const props = defineProps<{
|
||
visible: boolean
|
||
}>()
|
||
|
||
const emit = defineEmits<{
|
||
close: []
|
||
confirm: [imagePath: string]
|
||
}>()
|
||
|
||
const instance = getCurrentInstance()
|
||
const hasStroke = ref(false)
|
||
const canvasWidth = ref(300)
|
||
const canvasHeight = ref(500)
|
||
|
||
let ctx: UniApp.CanvasContext | null = null
|
||
let drawing = false
|
||
let lastX = 0
|
||
let lastY = 0
|
||
|
||
watch(
|
||
() => props.visible,
|
||
async (visible) => {
|
||
if (!visible)
|
||
return
|
||
|
||
await nextTick()
|
||
const query = uni.createSelectorQuery().in(instance)
|
||
query.select('#signature-canvas').boundingClientRect((rect) => {
|
||
if (rect && !Array.isArray(rect)) {
|
||
canvasWidth.value = rect.width
|
||
canvasHeight.value = rect.height
|
||
}
|
||
initCanvas()
|
||
}).exec()
|
||
},
|
||
)
|
||
|
||
function initCanvas() {
|
||
ctx = uni.createCanvasContext('signature-canvas', instance)
|
||
ctx.setStrokeStyle('#1a1a1a')
|
||
ctx.setLineWidth(3)
|
||
ctx.setLineCap('round')
|
||
ctx.setLineJoin('round')
|
||
clearCanvas()
|
||
}
|
||
|
||
function clearCanvas() {
|
||
if (!ctx)
|
||
return
|
||
|
||
ctx.clearRect(0, 0, canvasWidth.value, canvasHeight.value)
|
||
ctx.draw()
|
||
hasStroke.value = false
|
||
}
|
||
|
||
function getTouchPoint(event: TouchEvent) {
|
||
const touch = event.touches[0] || event.changedTouches[0]
|
||
return { x: touch.x, y: touch.y }
|
||
}
|
||
|
||
function handleTouchStart(event: TouchEvent) {
|
||
if (!ctx)
|
||
return
|
||
|
||
const point = getTouchPoint(event)
|
||
lastX = point.x
|
||
lastY = point.y
|
||
drawing = true
|
||
}
|
||
|
||
function handleTouchMove(event: TouchEvent) {
|
||
if (!drawing || !ctx)
|
||
return
|
||
|
||
const point = getTouchPoint(event)
|
||
ctx.beginPath()
|
||
ctx.moveTo(lastX, lastY)
|
||
ctx.lineTo(point.x, point.y)
|
||
ctx.stroke()
|
||
ctx.draw(true)
|
||
lastX = point.x
|
||
lastY = point.y
|
||
hasStroke.value = true
|
||
}
|
||
|
||
function handleTouchEnd() {
|
||
drawing = false
|
||
}
|
||
|
||
function handleConfirm() {
|
||
if (!hasStroke.value) {
|
||
uni.showToast({ title: '请先签名', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
uni.canvasToTempFilePath({
|
||
canvasId: 'signature-canvas',
|
||
success: (res) => {
|
||
emit('confirm', res.tempFilePath)
|
||
},
|
||
fail: () => {
|
||
emit('confirm', '')
|
||
},
|
||
}, instance)
|
||
}
|
||
|
||
function handleClose() {
|
||
emit('close')
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<view
|
||
v-if="visible"
|
||
class="signature-pad"
|
||
>
|
||
<view class="signature-pad__body">
|
||
<view class="signature-pad__actions">
|
||
<view
|
||
class="signature-pad__action-btn"
|
||
@tap="clearCanvas"
|
||
>
|
||
清除重写
|
||
</view>
|
||
<view
|
||
class="signature-pad__action-btn"
|
||
@tap="handleConfirm"
|
||
>
|
||
确认签署
|
||
</view>
|
||
</view>
|
||
|
||
<view class="signature-pad__canvas-wrap">
|
||
<canvas
|
||
id="signature-canvas"
|
||
canvas-id="signature-canvas"
|
||
class="signature-pad__canvas"
|
||
disable-scroll
|
||
@touchstart.stop.prevent="handleTouchStart"
|
||
@touchmove.stop.prevent="handleTouchMove"
|
||
@touchend.stop.prevent="handleTouchEnd"
|
||
/>
|
||
<text
|
||
v-if="!hasStroke"
|
||
class="signature-pad__placeholder"
|
||
>
|
||
我是签名
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view
|
||
class="signature-pad__close"
|
||
@tap="handleClose"
|
||
>
|
||
<text class="signature-pad__close-icon">
|
||
×
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<style scoped lang="scss">
|
||
@import './SignaturePad.scss';
|
||
</style>
|