当前位置: 首页 > news >正文

uniapp vue3 canvas实现手写签名

在这里插入图片描述

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;},});};// canvas 的errorconst 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);},});};// 如果想要base64格式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) => {// 通过文件系统读取 base64const 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;// background-color: #e7e5e7 !important;}.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')}
http://www.dtcms.com/a/355279.html

相关文章:

  • 【Node.js从 0 到 1:入门实战与项目驱动】2.4 开发工具推荐(VS Code 、IDEA及插件、终端工具、调试工具)
  • Cesium入门教程(二)环境搭建(HTML版)
  • QT:【第一个QT程序】【信号和槽】
  • 使用uni-app和Canvas生成简易双列表格布局并长按下载到本地
  • 混淆 打包 编译
  • Ovis2.5技术解密:原生分辨率与“反思模式”如何铸就新一代MLLM王者
  • 2024年山东省信息学小学组(CSP-X)第一轮题解
  • 冒泡排序算法详解(python code)
  • Python训练营打卡 DAY 50 预训练模型+CBAM模块
  • Shell 编程基础与实践要点梳理
  • PCIe 5.0 SSD连续读写缓存用完速度会骤降吗?
  • IntelliJ IDEA 反编译JAR包记录
  • Beats与Elasticsearch高效数据采集指南
  • Komo Searc-AI驱动的搜索引擎
  • 控制系统仿真之PID校正1-系统固有属性(四)
  • 【ai编辑器】使用cursor-vip获得cursor的pro版 pro plan(mac)
  • 【C语言16天强化训练】从基础入门到进阶:Day 13
  • 模拟实现Linux中的进度条
  • 带动态条件的模糊查询SQL
  • 【Linux基础知识系列:第一百一十四篇】使用lsof查看打开的文件
  • frp 一个高性能的反向代理服务
  • VMware + Ubuntu 桥接模式不能联网 的常见原因、排查思路和解决步骤
  • element-plus的el-scrollbar显示横向滚动条
  • 整体设计 修订 之1 三“先”之“基” 与范畴重构:康德先验哲学的批判性程序化实现
  • 电商高并发稳赢指南:ZKmall开源商城微服务架构的实战拆解
  • AI视觉重塑汽车质检,四大车间全景解析
  • Android15 GKI版本分析Kernel Crash问题
  • 金属超声波风速风向多参数一体传感器
  • NFT:Web3数字新资产
  • k230 使用摄像头将拍照的RGB565格式图片,保存为jpg图片文件到板载TF存储卡中