
userSign.vue
<template>
<view class="signature">
<view class="btn-box" v-if="orientation === 'abeam'">
<button @click="clearClick">重签</button>
<button @click="finish">完成签名</button>
</view>
<canvas id="canvas" canvas-id="canvas" :disable-scroll="true" @touchmove="move" @touchstart="start" @error="error"
@touchend="touchend" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }">
</canvas>
<view class="btn-box" v-if="orientation === 'portrait'">
<button type="warn" @click="clearClick">重签</button>
<button type="primary" @click="finish">完成签名</button>
</view>
</view>
</template>
<script setup>
import {
ref,
onMounted,
getCurrentInstance
} from 'vue'
const props = defineProps({
orientation: {
type: String,
default: "portrait",
},
width: {
type: Number,
default: 0,
},
height: {
type: Number,
default: 0,
},
lineWidth: {
type: Number,
default: 3,
},
strokeStyle: {
type: String,
default: "black",
},
});
const emit = defineEmits(["finish", "clear"]);
const instance = getCurrentInstance().proxy;
const ctx = ref("");
const pr = ref(0);
const canvasWidth = ref("");
const canvasHeight = ref("");
const points = ref([]);
onMounted(() => {
getSystemInfo();
createCanvas();
});
const start = (e) => {
points.value.push({
X: e.touches[0].x,
Y: e.touches[0].y
});
ctx.value.beginPath();
};
const move = (e) => {
points.value.push({
X: e.touches[0].x,
Y: e.touches[0].y
});
draw();
};
const touchend = () => {
points.value = [];
};
const draw = () => {
const point1 = points.value[0];
const point2 = points.value[1];
points.value.shift();
ctx.value.moveTo(point1.X, point1.Y);
ctx.value.lineTo(point2.X, point2.Y);
ctx.value.stroke();
ctx.value.draw(true);
};
const createCanvas = () => {
ctx.value = uni.createCanvasContext("canvas", instance, {
willReadFrequently: true,
});
ctx.value.lineGap = "round";
ctx.value.lineJoin = "round";
ctx.value.lineWidth = props.lineWidth;
ctx.value.strokeStyle = props.strokeStyle;
};
const canvasW = ref(300);
const canvasH = ref(300);
const getSystemInfo = () => {
uni.getSystemInfo({
success: (res) => {
pr.value = res.pixelRatio;
if (props.orientation == "portrait") {
if (props.width > res.windowWidth || props.width == 0) {
canvasWidth.value = res.windowWidth;
} else {
canvasWidth.value = props.width;
}
if (props.height > res.windowHeight - 70 || props.height == 0) {
canvasHeight.value = res.windowHeight - 70;
} else {
canvasHeight.value = props.height;
}
} else if (props.orientation == "abeam") {
if (props.width > res.windowWidth - 70 || props.width == 0) {
canvasWidth.value = res.windowWidth - 70;
} else {
canvasWidth.value = props.width;
}
if (props.height > res.windowHeight || props.height == 0) {
canvasHeight.value = res.windowHeight;
} else {
canvasHeight.value = props.height;
}
}
canvasHeight.value = 300;
const rate = canvasHeight.value / canvasWidth.value;
canvasW.value = 300;
canvasH.value = 300 / rate;
},
});
};
const error = (e) => {
console.log("画出错了" + e);
};
const clearClick = () => {
ctx.value.clearRect(0, 0, canvasWidth.value, canvasHeight.value);
ctx.value.draw(true);
emit("clear");
};
const finish = () => {
uni.canvasToTempFilePath({
canvasId: "canvas",
success: (res) => {
const path = res.tempFilePath;
emit("finish", path);
},
});
};
const finish = () => {
uni.canvasToTempFilePath({
canvasId: "canvas",
x: 0,
y: 0,
width: canvasWidth.value,
height: canvasHeight.value,
destWidth: canvasWidth.value * pr.value,
destHeight: canvasHeight.value * pr.value,
fileType: 'png',
quality: 1,
success: (res) => {
const base64 = uni.getFileSystemManager().readFileSync(res.tempFilePath, 'base64')
console.log('base64=', `data:image/png;base64,${base64}`);
emit('finish', `data:image/png;base64,${base64}`)
},
fail: (err) => {
console.error('生成签名失败:', err)
}
}, instance);
};
defineExpose({
clearClick,
});
</script>
<style scoped lang="scss">
canvas {
background-color: white;
}
.signature {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
align-items: flex-end;
}
.btn-box {
width: 100%;
display: flex;
text-align: center;
padding: 20rpx 0;
border-top: 1px solid #bbb;
}
</style>
使用它
.popup-title {
text-align: center;
font-weight: 500;
font-weight: bold;
padding: 40rpx 0;
border-bottom: 1px solid #bbb;
}
<u-popup ref="popupRef" mode="center" title="考试签名" background-color="#fff">
<view class="popup-title">
<text>考试签名</text>
</view>
<view>
<userSign></userSign>
</view>
</u-popup>
const popupRef = ref()
const togglePopup = () => {
console.log('悬浮球 - 弹框出答题卡');
popupRef.value.open('center')
}