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.mzph.cn/pingmian/65772.shtml

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

相关文章

Go+chromedp实现Web UI自动化测试

1.为什么使用go进行UI自动化测试&#xff1f; 速度&#xff1a;Go速度很快&#xff0c;这在运行包含数百个UI测试的测试套件时是一个巨大的优势 并发性&#xff1a;可以利用Go的内置并发性(goroutines)来并行化测试执行 简单&#xff1a;Go的简约语法允许您编写可读且可维护…

【LLM综述】29种大模型Prompt Engineering技术

note 从零样本&#xff08;Zero-shot&#xff09;提示到最新进展的各种提示技术&#xff0c;包括推理和逻辑链&#xff08;Chain-of-Thought, CoT&#xff09;提示、自动链式思考&#xff08;Auto-CoT&#xff09;提示、自我一致性&#xff08;Self-Consistency&#xff09;提…

基于SpringBoot的实验室信息管理系统【源码+文档+部署讲解】

系统介绍 视频演示 基于SpringBootVue实现的实验室信息管理系统采用前后端分离的架构方式&#xff0c;系统分为管理员、老师、用户三种角色&#xff0c;实现了用户管理、设备管理、实验室查询、公告、课程、实验室耗材管理、我的等功能 技术选型 开发工具&#xff1a;idea2…

arcgis模版空库怎么用(一)

这里以某个项目的数据为例&#xff1a; 可以看到&#xff0c;属性表中全部只有列标题&#xff0c;无数据内容 可能有些人会认为空库是用来往里面加入信息的&#xff0c;其实不是&#xff0c;正确的用法如下&#xff1a; 一、下图是我演示用的数据&#xff0c;我们可以看到其中…

基于Spring Boot + Vue3实现的在线汽车保养维修预约管理系统源码+文档

前言 基于Spring Boot Vue3实现的在线汽车保养维修预约管理系统是一种前后端分离架构的应用&#xff0c;它结合了Java后端开发框架Spring Boot和现代JavaScript前端框架Vue.js 3.0的优势。这样的系统可以为汽车服务站提供一个高效的平台来管理客户的预约请求 技术选型 系统…

2024年总结

2024年是努力打破思维边界的一年&#xff0c;为了突破自己&#xff0c;努力阅读更多的书籍&#xff0c;变换更多的角度思考问题&#xff1b; 努力的写博客&#xff0c;写文字&#xff0c;总结自己的思想&#xff1b; 不断的实践&#xff0c;克制自己的思维和行动的惯性&#…

论文研读:Text2Video-Zero 无需微调,仅改动<文生图模型>推理函数实现文生视频(Arxiv 2023-03-23)

论文名&#xff1a;Text2Video-Zero: Text-to-Image Diffusion Models are Zero-Shot Video Generators 1. 摘要 1.1 方法总结 通过潜空间插值, 实现动作连续帧。 以第一帧为锚定&#xff0c;替换原模型的self-attention&#xff0c;改为cross-attention 实现 保证图片整体场…

python 归并排序(Merge Sort)

归并排序&#xff08;Merge Sort&#xff09; 归并排序是一种高效的排序算法&#xff0c;采用分治法&#xff08;Divide and Conquer&#xff09;策略。它的基本思想是&#xff1a;将数组递归地分成两半&#xff0c;分别对两半进行排序&#xff0c;然后将排序后的两半合并成一…

AI安全的挑战:如何让人工智能变得更加可信

引言 随着人工智能&#xff08;AI&#xff09;技术在各个领域的广泛应用&#xff0c;尤其是在医疗、金融、自动驾驶和智能制造等行业&#xff0c;AI正在重塑我们的工作和生活方式。从提高生产效率到实现个性化服务&#xff0c;AI带来了前所未有的便利。然而&#xff0c;在享受这…

去除el-tabs 下面的灰色横线,并修改每一项的左右间距,和字体颜色

HTML <el-tabs v-model"activeName" class"demo-tabs" tab-click"handleClick"><el-tab-pane label"全部" :name"null"></el-tab-pane><el-tab-pane label"问答陪练" name"general-t…

C++算法20例

1、求两个数的最大公约数 int gcd(int a, int b) { 2 return b 0 ? a : gcd(b, a % b); 3} 2、判断素数 bool isPrime(int n) {if (n < 1) return false; for (int i 2; i * i < n; i) {if (n % i 0) return false;}return true; } 3、冒泡排序 void bubbleSort…

截图技术方案

安卓截屏技术附带悬浮窗自动存储功能_安卓截图浮窗-CSDN博客 https://chat.baidu.com/search?dyTabStrMCwxMiwzLDEsMiwxMyw3LDYsNSw5&pdcsaitab&setypecsaitab&extParamsJson%7B%22apagelid%22%3A%2210990774271994514433%22%2C%22enter_type%22%3A%22a_ai_index%…

气象数据Grib及Python绘图

文章较长&#xff0c;却将所有常见的气象数据类型进行了详细的介绍&#xff0c;对各种方法的优劣势进行了详细分析&#xff0c;相信对于阅读者来说会有一定程度的帮助 目录 GRIB 数据格式简介 使用Python处理Grib文件 法1&#xff1a;使用pygrib库 法2&#xff1a;使用cf…

sqlite3 python如何查表结构

在SQLite3中&#xff0c;通过Python的sqlite3模块可以方便地查询表结构。以下是几种常用的方法来获取表结构信息&#xff0c;包括列名、数据类型和其他属性。 使用 PRAGMA table_info 最直接的方法是使用 PRAGMA table_info 命令。这个命令会返回指定表中每一列的信息&#x…

Selenium和WebDriver的安装与配置

1、Selenium的安装 直接黑窗口执行&#xff1a;pip install selenium3.141.0 可能遇到的问题&#xff1a; 解决方法配置环境变量&#xff1a; 找到目录&#xff1a;&#xff08;以自己电脑为准&#xff09; C:\Users\Administrator\AppData\Local\Programs\Python\Python38-…

HTML——45.单元格合并

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>表格</title></head><body><!--合并单元格&#xff1a;1.在代码中找到要合并的单元格2.在要合并的所有单元格中&#xff0c;保留要合并的第一个单元格…

APP项目测试 之 APP性能测试-- 性能测试工具(SoloPi工具)

1.SoloPi简介 &#xff08;1&#xff09;什么是SoloPi&#xff1f; SoloPi&#xff1a; 是一个无线化、非侵入式的 Android 自动化工具 &#xff0c;具备 录制回放、性能测试 等功能。 &#xff08;2&#xff09;SoloPi的作用是什么&#xff1f; 基础性能测试&#xff1a;能够…

chatgpt model spec 2024

概述 这是模型规范的初稿&#xff0c;该文档规定了我们在OpenAI API和ChatGPT中的模型的期望行为。它包括一组核心目标&#xff0c;以及关于如何处理冲突目标或指令的指导。 我们打算将模型规范作为研究人员和数据标注者创建数据的指南&#xff0c;这是一种称为从人类反馈中进…

IOS safari 播放 mp4 遇到的坎儿

起因 事情的起因是调试 IOS 手机下播放服务器接口返回的 mp4 文件流失败。对于没调试过移动端和 Safari 的我来说着实费了些功夫&#xff0c;网上和AI也没有讲明白。好在最终大概理清楚了&#xff0c;在这里整理出来供有缘人参考。 问题 因为直接用 IOS 手机的浏览器打开页面…

Mac 环境 VVenC 编译与编码命令行工具使用教程

VVenC VVenC 是一个开源的高效视频编码器&#xff0c;专门用于支持 H.266/VVC (Versatile Video Coding) 标准的编码。H.266/VVC 是继 HEVC (H.265) 之后的新一代视频编码标准&#xff0c;主要目的是提供比 HEVC 更高的压缩效率&#xff0c;同时保持或提高视频质量。H.266/VVC…