uniapp3 手写签名组件(vue3 语法)封装与应用

本文介绍了基于 uniapp3(vue3 语法)封装的手写签名组件。
包括父组件的调用方式,如通过条件判断展示签名图片或点击进入签名页面,以及接收签名照片的逻辑。子组件涵盖了自定义导航栏、清除、取消、确认等操作按钮,利用 canvas 实现手写签名功能,包括笔迹绘制、颜色选择、重写、图片旋转与导出等操作,同时涉及获取系统信息设置 canvas 尺寸和背景色等关键技术点,为在 uniapp3 项目中实现手写签名功能提供了完整的解决方案。


父组件 调用方式 :

<template><view style="width: 100%; padding: 0 10px"><img class="sign-img" v-if="pageData.tempFilePath" :src="pageData.tempFilePath" /><view class="sign-header" v-else @click="goSign">点击输入您的签名</view></view><viewstyle="width: 100vw;height: 100vh;position: fixed;z-index: 9999;top: 0;left: 0;background-color: #fff;"v-if="pageData.showSign"><signaturePlugin@getTempFilePath="getTempFilePath"@close="closeSign"style="width: 100%; height: 100%"/></view>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import signaturePlugin from '@/components/signature.vue'
const pageData = reactive({tempFilePath: '',showSign: false
})
const closeSign = function () {pageData.showSign = false
}
// 点击重写,进入签名页面
const goSign = function () {pageData.showSign = true
}
// 签名页面返回回来,接收签名照片展示
const getTempFilePath = function (data) {let { tempFilePath } = JSON.parse(data)pageData.tempFilePath = tempFilePathconsole.log('签名页面返回回来,接收签名照片展示', tempFilePath)
}
</script>
<style scope>
.sign-header {display: flex;justify-content: center;align-items: center;color: grey;border: 1px solid #e6e6e6;border-radius: 8px;background-color: #fff;height: 300rpx;margin: 0 10px;
}.sign-img {border: 1px solid #e6e6e6;border-radius: 8px;height: 300rpx;width: 100%;
}
</style>

子组件(手写签名组件)

<template><view><!-- 自定义导航栏 --><!-- <NaviBar title="签署" :autoBack="true" /> --><view class="wrapper"><view class="handBtn"><button @click="retDraw" class="delBtn">清除</button><button @click="saveCanvasAsImg" class="saveBtn">取消</button><button @click="subCanvas" class="subBtn">确认</button></view><view class="handCenter" ref="handCenter"><canvasclass="handWriting":disable-scroll="true"@touchstart="uploadScaleStart"@touchmove="uploadScaleMove"canvas-id="handWriting"/><!--用于旋转图片的canvas容器--><canvasstyle="position: absolute":style="{ width: cavWidth + 'px', height: cavWidth1 + 'px' }"canvas-id="handWriting2"></canvas></view></view></view>
</template><script setup>
import { ref, onMounted, reactive, getCurrentInstance, nextTick, defineEmits } from 'vue'
const instance = getCurrentInstance()
// 定义触发的事件及其数据类型
const emit = defineEmits(['close', 'getTempFilePath'])
const canvasName = ref('handWriting')
const ctx = ref('')
const startX = ref(null)
const startY = ref(null)
const canvasWidth = ref(0)
const canvasHeight = ref(0)
const selectColor = ref('black')
const lineColor = ref('#1A1A1A') // 颜色
const canvas = ref(null)
const cavWidth = ref(1000)
const cavWidth1 = ref(1000)
const lineSize = ref(5) // 笔记倍数
const location = ref(null)
const handCenter = ref(null)onMounted(() => {ctx.value = uni.createCanvasContext('handWriting', instance.proxy)uni.getSystemInfo({success: function (res) {const windowWidth = res.windowWidth // 窗口宽度const windowHeight = res.windowHeight // 窗口高度console.log(windowWidth)console.log(windowHeight)cavWidth.value = canvasWidth.value = windowWidthcavWidth1.value = canvasHeight.value = windowHeightsetCanvasBg('#fff')},})
})// 笔迹开始
const uploadScaleStart = (e) => {startX.value = e.changedTouches[0].xstartY.value = e.changedTouches[0].y//设置画笔参数//画笔颜色ctx.value.setStrokeStyle(lineColor.value)//设置线条粗细ctx.value.setLineWidth(lineSize.value)//设置线条的结束端点样式ctx.value.setLineCap('round') //'butt'、'round'、'square'//开始画笔ctx.value.beginPath()
}// 笔迹移动
const uploadScaleMove = (e) => {//取点let temX = e.changedTouches[0].xlet temY = e.changedTouches[0].y//画线条ctx.value.moveTo(startX.value, startY.value)ctx.value.lineTo(temX, temY)ctx.value.stroke()startX.value = temXstartY.value = temYctx.value.draw(true)
}// 重写
const retDraw = () => {ctx.value.clearRect(0, 0, 700, 730)ctx.value.draw()//设置canvas背景setCanvasBg('#fff')
}// 选择颜色
const selectColorEvent = (str, color) => {selectColor.value = strlineColor.value = color
}// 确认
const subCanvas = () => {uni.canvasToTempFilePath({canvasId: 'handWriting',fileType: 'png',quality: 1, //图片质量success: (res) => {console.log(res.tempFilePath, 'canvas生成图片地址')wx.getImageInfo({// 获取图片的信息src: res.tempFilePath,success: (res1) => {console.log('res1', res1)// 将canvas1的内容复制到canvas2中let canvasContext = uni.createCanvasContext('handWriting2', instance)let rate = res1.height / res1.widthlet width = 300 / ratelet height = 300cavWidth.value = heightcavWidth1.value = widthcanvasContext.translate(height / 2, width / 2)canvasContext.rotate((270 * Math.PI) / 180)canvasContext.drawImage(res.tempFilePath, -width / 2, -height / 2, width, height)console.log(0, canvasContext)canvasContext.draw(false, () => {// 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中uni.canvasToTempFilePath({// 把当前画布指定区域的内容导出生成指定大小的图片。在 draw() 回调里调用该方法才能保证图片导出成功。canvasId: 'handWriting2',fileType: 'png',quality: 1, //图片质量success: (res2) => {console.log('res2', res2)let data = JSON.stringify({tempFilePath: res2.tempFilePath,})emit('getTempFilePath', data)emit('close')},},instance,)})},})},},instance,)
}//旋转图片,生成新canvas实例
const rotate = (cb) => {wx.createSelectorQuery().select('#handWriting2').fields({node: true,size: true,}).exec((res) => {const rotateCanvas = res[0].nodeconst rotateCtx = rotateCanvas.getContext('2d')//this.ctxW-->所绘制canvas的width//this.ctxH -->所绘制canvas的heightrotateCanvas.width = canvasHeight.valuerotateCanvas.height = canvasWidth.valuewx.canvasToTempFilePath({canvas: canvas.value,success: (res) => {const img = rotateCanvas.createImage()img.src = res.tempFilePathimg.onload = function () {rotateCtx.translate(rotateCanvas.width / 2, rotateCanvas.height / 2)rotateCtx.rotate((270 * Math.PI) / 180)rotateCtx.drawImage(img, -rotateCanvas.height / 2, -rotateCanvas.width / 2)rotateCtx.scale(1, 1)cb(rotateCanvas)}},fail: (err) => {console.log(err)},},instance,)})
}//取消
const saveCanvasAsImg = () => {retDraw()// uni.navigateBack()emit('close')
}//设置canvas背景色  不设置  导出的canvas的背景为透明
//@params:字符串  color
const setCanvasBg = (color) => {/* 将canvas背景设置为 白底,不设置  导出的canvas的背景为透明 *///rect() 参数说明  矩形路径左上角的横坐标,左上角的纵坐标, 矩形路径的宽度, 矩形路径的高度//这里是 canvasHeight - 4 是因为下边盖住边框了,所以手动减了写ctx.value.rect(0, 0, canvasWidth.value, canvasHeight.value - 4)ctx.value.setFillStyle(color)ctx.value.fill() //设置填充ctx.value.draw() //开画
}
</script><style>
page {background: #fbfbfb;height: auto;overflow: hidden;
}.wrapper {position: relative;width: 100%;height: 100vh;margin: 20rpx 0;overflow: hidden;display: flex;align-content: center;flex-direction: row;justify-content: center;font-size: 28rpx;
}.handWriting {background: #fff;width: 100%;height: 100vh;
}.handCenter {border-left: 2rpx solid #e9e9e9;flex: 5;overflow: hidden;box-sizing: border-box;
}.handBtn button {font-size: 28rpx;
}.handBtn {height: 100vh;display: inline-flex;flex-direction: column;justify-content: space-between;align-content: space-between;flex: 1;
}.delBtn {width: 200rpx;position: absolute;bottom: 350rpx;left: -35rpx;transform: rotate(90deg);color: #666;
}.subBtn {width: 200rpx;position: absolute;bottom: 52rpx;left: -35rpx;display: inline-flex;transform: rotate(90deg);background: #29cea0;color: #fff;margin-bottom: 60rpx;text-align: center;justify-content: center;
}/*Peach - 新增 - 保存*/.saveBtn {width: 200rpx;position: absolute;bottom: 590rpx;left: -35rpx;transform: rotate(90deg);color: #666;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/500434.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【笔记】在虚拟机中通过apache2给一个主机上配置多个web服务器

&#xff08;配置出来的web服务器又叫虚拟主机……&#xff09; 下载apache2 sudo apt update sudo apt install apache2 &#xff08;一&#xff09;ip相同 web端口不同的web服务器 进入 /var/www/html 创建站点一和站点二的目录文件&#xff08;目录文件名自定义哈&#x…

linux装git

前言 以 deepin 深度系统为例&#xff0c;安装命 令行版 Git 非常简单。 安装 注意&#xff1a;需要输入账号密码&#xff0c;否则无法进行。 打开终端&#xff0c;执行如下命令即可。 sudo apt-get install git成功 如下图所示&#xff0c;输入 git &#xff0c;命令识别即…

【Spark】架构与核心组件:大数据时代的必备技能(下)

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《大数据前沿&#xff1a;技术与应用并进》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、什么是Apache Spark 2、Spark 的应用场景&…

NLP中的神经网络基础

一&#xff1a;多层感知器模型 1&#xff1a;感知器 解释一下&#xff0c;为什么写成 wxb>0 &#xff0c;其实原本是 wx > t ,t就是阈值&#xff0c;超过这个阈值fx就为1&#xff0c;现在把t放在左边。 在感知器里面涉及到两个问题&#xff1a; 第一个&#xff0c;特征提…

第十一章 图论

题目描述&#xff1a; 阿里这学期修了计算机组织和架构课程。他了解到指令之间可能存在依赖关系&#xff0c;比如WAR&#xff08;读后写&#xff09;、WAW、RAW。 如果两个指令之间的距离小于安全距离&#xff0c;则会导致危险&#xff0c;从而可能导致错误的结果。因此&#…

嵌入式系统 第七讲 ARM-Linux内核

• 7.1 ARM-Linux内核简介 • 内核&#xff1a;是一个操作系统的核心。是基于硬件的第一层软件扩充&#xff0c; 提供操作系统的最基本的功能&#xff0c;是操作系统工作的基础&#xff0c;它负责管理系统的进程、内存、设备驱动程序、文件和网络系统&#xff0c; 决定着系统的…

win11蓝屏死机 TPM-WMI

1. 打开win11的事件查看器&#xff0c;定位错误 最近两次都是 KB5016061&#xff1a;安全启动数据库和 DBX 变量更新事件 - Microsoft 支持 事件源 TPM-WMI 事件 ID 1796 2. 解决方案 打开注册表&#xff1a;计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Contro…

Linux命令——3.网络与用户

文章目录 一、网络1.网络测试与诊断2.网络接口配置3.无线网络配置4.防火墙与网络管理6.防火墙管理1&#xff09;firewalld命令2&#xff09;iptables命令 二、用户和群组1.管理员模式2.用户账户管理1&#xff09;useradd创建2&#xff09;usermod修改3&#xff09;userdel 删除…

机器学习算法基础知识1:决策树

机器学习算法基础知识1&#xff1a;决策树 一、本文内容与前置知识点1. 本文内容2. 前置知识点 二、场景描述三、决策树的训练1. 决策树训练方式&#xff08;1&#xff09;分类原则-Gini&#xff08;2&#xff09;分类原则-entropy&#xff08;3&#xff09;加权系数-样本量&am…

_使用CLion的Vcpkg安装SDL2,添加至CMakelists时报错,编译报错

语言&#xff1a;C20 编译器&#xff1a;gcc 14.2 摘要&#xff1a;初次使用Vcpkg添加SDL2&#xff0c;出现CMakelists找不到错误、编译缺失main错误、运行失败错误。 CMakelists缺失错误&#xff1a; 使用CLion的Vcpkg安装SDL2时&#xff0c;按照指示把对应代码添加至CMakel…

Lumos学习王佩丰Excel第二十二讲:制作甘特图与动态甘特图

一、制作双向条形图 1. 分离坐标轴 2. 自定义坐标轴数字格式&#xff1a;加分号加正常数字 3. 修改图表背景 修改图片艺术效果&#xff1a;虚化图片 二、制作甘特图 1、甘特图定义 甘特图&#xff08;Gantt chart&#xff09;又称为横道图、条状图&#xff08;Bar chart&…

el-pagination 为什么只能展示 10 条数据(element-ui@2.15.13)

好的&#xff0c;我来帮你分析前端为什么只能展示 10 条数据&#xff0c;以及如何解决这个问题。 问题分析&#xff1a; pageSize 的值&#xff1a; 你的 el-pagination 组件中&#xff0c;pageSize 的值被设置为 10&#xff1a;<el-pagination:current-page"current…

【网络安全实验室】SQL注入实战详情

如果额头终将刻上皱纹&#xff0c;你只能做到&#xff0c;不让皱纹刻在你的心上 1.最简单的SQL注入 查看源代码&#xff0c;登录名为admin 最简单的SQL注入&#xff0c;登录名写入一个常规的注入语句&#xff1a; 密码随便填&#xff0c;验证码填正确的&#xff0c;点击登录…

ruoyi 多租户 开启后针对某一条sql不适用多租户; 若依多租户sql规则修改

文章参考&#xff1a;多租户功能 | Ruoyi-TDesign 忽略租户​ 1.如果需要指定单独 SQL 不开启过滤&#xff0c;可在对应的 Mapper 接口添加如下忽略注解&#xff1a; InterceptorIgnore(tenantLine "true", dataPermission "false") 此处注意事项 使…

一文理解条件竞争漏洞

视频教程在我主页简介或专栏里 目录&#xff1a; 理解竞争条件的基本概念 限制超越型竞争条件 使用 Burp Repeater 检测和利用限制超限竞态条件 方法论 1 — 预测潜在的冲突 2 — 线索 3 — 概念验证 如何防止竞态条件漏洞 理解竞争条件的基本概念 竞争条件(也就是条件竞…

一种基于动态部分重构的FPGA自修复控制器

1.FPGA动态部分重构技术 动态部分重构技术指在FPGA运行时&#xff0c;通过加载部分位流文件来修改FPGA可重构区域中的逻辑设计&#xff0c;修改过程中其余逻辑功能不受影响整个系统也能够持续运行。 下图为FPGA动态部分重构的基本原理图。通过下载A1.bit、A2.bit、A3.bit 或A4.…

计算机网络体系结构基础知识

一、计算机网络的两个目标&#xff1a; ①两台计算机之间通信 ②两台计算机之间的资源共享 二、计算机网络概述 1.定义&#xff1a;利用通信线路将地理上分散的、具有独立功能的计算机系统和通信设备按不同 的形式连接起来&#xff0c;以功能完善的网络软件及协…

云计算学习架构篇之HTTP协议、Nginx常用模块与Nginx服务实战

一.HTTP协议讲解 1.1rsync服务重构 bash 部署服务端: 1.安装服务 [rootbackup ~]# yum -y install rsync 2.配置服务 [rootbackup ~]# vim /etc/rsyncd.conf uid rsync gid rsync port 873 fake super yes use chroot no max connections 200 timeout 600 ignore erro…

合合信息亮相CSIG AI可信论坛,全面拆解AI视觉内容安全的“终极防线”

合合信息亮相CSIG AI可信论坛&#xff0c;全面拆解视觉内容安全的“终极防线”&#xff01; &#x1f42f; AI伪造泛滥&#xff0c;我们还能相信“眼见为实”吗&#xff1f; 近期&#xff0c;由中国图象图形学学会主办的CSIG青年科学家会议 AI可信论坛在杭州成功举办。本次论…

AI 智能助手对话系统

一个基于 React 和 Tailwind CSS 构建的现代化 AI 对话系统&#xff0c;提供流畅的用户体验和丰富的交互功能。 项目链接&#xff1a;即将开放… 功能特点 &#x1f916; 智能对话&#xff1a;支持与 AI 助手实时对话&#xff0c;流式输出回答&#x1f4c1; 文件处理&#xff…