
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')}