uni-app 微信小程序 电子签名及签名图片翻转显示功能

news2025/6/13 17:38:24

文章目录

    • 1. 需求背景
    • 2. 开始撸
      • 2.1 点击 重写 进入签名页面(上图一)
      • 2.2 书写签名,点击确认返回,及图片翻转显示(上图二,三)
    • 3. 图片进行翻转,返回翻转后的图片

1. 需求背景

接的一个开发一个小程序,需求很简单,使用uni-app实现一个微信小程序的电子签名功能请添加图片描述

2. 开始撸

2.1 点击 重写 进入签名页面(上图一)

在这里插入图片描述

<template>
	<view>
		<view class="ft-26 color-red mt-20 mb-20">
			本人承诺以上检查内容真实
		</view>
		<view class="sign">
			<view class="sign-header">
				<span><i class="color-red">*</i> 本人签名</span>
				<div @click="goSign">
					<img class="edit-icon" :src="require('@/static/images/edit.png')" alt="">
					<label for="">重写</label>
				</div>
			</view>
			<img class="sign-img" :src="tempFilePath">
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				tempFilePath: "",
			}
		},
		methods: {
			// 点击重写,进入签名页面
			goSign() {
				uni.navigateTo({
					url: `/examine/q-sign`
				})
			},
			// 签名页面返回回来,接收签名照片展示
			getTempFilePath(data) {
				let { tempFilePath } = JSON.parse(data)
				this.tempFilePath = tempFilePath
			},
		},
	}
</script>

<style lang="scss" scoped>
	.report-view {
		height: 50vh;
		background: #fff;
	}

	.popup-content {
		width: 100vw;
		height: 100vh;
	}

	.sign {
		border-radius: 10rpx;
		border: 1rpx solid #E6E6E6;
		overflow: hidden;

		.sign-header {
			line-height: 56rpx;
			background: #E8EFF8;
			border-radius: 0px;
			display: flex;
			justify-content: space-between;
			padding: 0 20rpx;
			font-size: 26rpx;
			display: flex;
			align-items: center;

			.edit-icon {
				width: 24rpx;
				height: 24rpx;
				display: inline-block;
				margin-right: 5rpx;
			}

			span {
				i {
					line-height: 56rpx;
					display: inline-block;
					margin-right: 10rpx;
				}
			}

			text {
				font-weight: 500;
				color: #999999;
			}
		}

		.sign-img {
			width: 100%;
			height: 300rpx;
			background: #fff;
		}
	}
</style>

2.2 书写签名,点击确认返回,及图片翻转显示(上图二,三)

在这里插入图片描述
完整代码

<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">
				<canvas class="handWriting" :disable-scroll="true" @touchstart="uploadScaleStart"
					@touchmove="uploadScaleMove" canvas-id="handWriting" />
				<!--用于旋转图片的canvas容器-->
				<canvas style="position: absolute" :style="{ width: cavWidth, height: cavWidth1 }"
					canvas-id="handWriting2"></canvas>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		name: 'Signature',
		data() {
			return {
				canvasName: 'handWriting',
				ctx: '',
				startX: null,
				startY: null,
				canvasWidth: 0,
				canvasHeight: 0,
				selectColor: 'black',
				lineColor: '#1A1A1A', // 颜色
				canvas: null,
				cavWidth: 2000,
				cavWidth1: 2000,
				lineSize: 5, // 笔记倍数
			}
		},
		onLoad({
			location
		}) {
			if (location) {
				this.location = location;
			}
			this.ctx = uni.createCanvasContext('handWriting', this)
			this.$nextTick(() => {
				uni
					.createSelectorQuery()
					.select('.handCenter')
					.boundingClientRect((rect) => {
						this.canvasWidth = rect.width
						this.canvasHeight = rect.height
						/* 将canvas背景设置为 白底,不设置  导出的canvas的背景为透明 */
						this.setCanvasBg('#fff')
					})
					.exec()
			})
		},
		methods: {
			// 笔迹开始
			uploadScaleStart(e) {
				this.startX = e.changedTouches[0].x
				this.startY = e.changedTouches[0].y
				//设置画笔参数
				//画笔颜色
				this.ctx.setStrokeStyle(this.lineColor)
				//设置线条粗细
				this.ctx.setLineWidth(this.lineSize)
				//设置线条的结束端点样式
				this.ctx.setLineCap('round') //'butt'、'round'、'square'
				//开始画笔
				this.ctx.beginPath()
			},
			// 笔迹移动
			uploadScaleMove(e) {
				//取点
				let temX = e.changedTouches[0].x
				let temY = e.changedTouches[0].y
				//画线条
				this.ctx.moveTo(this.startX, this.startY)
				this.ctx.lineTo(temX, temY)
				this.ctx.stroke()
				this.startX = temX
				this.startY = temY
				this.ctx.draw(true)
			},
			/**
			 * 重写
			 */
			retDraw() {
				this.ctx.clearRect(0, 0, 700, 730)
				this.ctx.draw()
				//设置canvas背景
				this.setCanvasBg('#fff')
			},
			/**
			 * @param {Object} str
			 * @param {Object} color
			 * 选择颜色
			 */
			selectColorEvent(str, color) {
				this.selectColor = str
				this.lineColor = color
			},
			// 确认
			subCanvas() {
				const _this = this
				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)
								// 将canvas1的内容复制到canvas2中
								let canvasContext = wx.createCanvasContext('handWriting2')
								let rate = res1.height / res1.width
								let width = 300 / rate
								let height = 300
								_this.cavWidth = 300 / rate
								_this.cavWidth1 = 300
								canvasContext.translate(height / 2, width / 2)
								canvasContext.rotate((270 * Math.PI) / 180)
								canvasContext.drawImage(res.tempFilePath, -width / 2, -height / 2,
									width, height)
								canvasContext.draw(false, () => {
									// 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中
									wx.canvasToTempFilePath({
										// 把当前画布指定区域的内容导出生成指定大小的图片。在 draw() 回调里调用该方法才能保证图片导出成功。
										canvasId: 'handWriting2',
										fileType: 'png',
										quality: 1, //图片质量
										success(res2) {
											let data = JSON.stringify({
												tempFilePath: res2
													.tempFilePath,
											})
											let pages = getCurrentPages();
											let prevPage = pages[pages.length - 2];
											prevPage.$vm.getTempFilePath(data)
											uni.navigateBack({
												delta: 1
											})
										}
									})
								})
							}
						})
					},
				})
			},
			//旋转图片,生成新canvas实例
			rotate(cb) {
				const that = this
				wx.createSelectorQuery()
					.select('#handWriting2')
					.fields({
						node: true,
						size: true
					})
					.exec((res) => {
						const rotateCanvas = res[0].node
						const rotateCtx = rotateCanvas.getContext('2d')
						//this.ctxW-->所绘制canvas的width
						//this.ctxH -->所绘制canvas的height
						rotateCanvas.width = this.ctxH
						rotateCanvas.height = this.ctxW
						wx.canvasToTempFilePath({
							canvas: that.canvas,
							success(res) {
								const img = rotateCanvas.createImage()
								img.src = res.tempFilePath
								img.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(that.pixelRatio, that.pixelRatio)
									cb(rotateCanvas)
								}
							},
							fail(err) {
								console.log(err)
							}
						})
					})
			},
			//取消
			saveCanvasAsImg() {
				this.retDraw()
				uni.navigateBack()
			},
			//设置canvas背景色  不设置  导出的canvas的背景为透明
			//@params:字符串  color
			setCanvasBg(color) {
				/* 将canvas背景设置为 白底,不设置  导出的canvas的背景为透明 */
				//rect() 参数说明  矩形路径左上角的横坐标,左上角的纵坐标, 矩形路径的宽度, 矩形路径的高度
				//这里是 canvasHeight - 4 是因为下边盖住边框了,所以手动减了写
				this.ctx.rect(0, 0, this.canvasWidth, this.canvasHeight - 4)
				// ctx.setFillStyle('red')
				this.ctx.setFillStyle(color)
				this.ctx.fill() //设置填充
				this.ctx.draw() //开画
			},
			toJSON() {}
		}
	}
</script>

<style>
	page {
		background: #fbfbfb;
		height: auto;
		overflow: hidden;
	}

	.wrapper {
		position: relative;
		width: 100%;
		height: 100vh;
		margin: 20rpx 0;
		overflow: auto;
		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>

3. 图片进行翻转,返回翻转后的图片

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

结构体||联合体

1.结构体 1.1实际生活中一些东西往往有多个元素组成。如一名学生有身高、体重、名字、学号等。这时候就需要用到结构体。 结构体是一些值的结合&#xff0c;这些值被称为成员变量。结构体的每个成员可以是不同类型的变量&#xff0c;如&#xff1a;标量、数组、指针、甚至是其…

锐捷EWEB网管系统 RCE漏洞复现

0x01 产品简介 锐捷网管系统是由北京锐捷数据时代科技有限公司开发的新一代基于云的网络管理软件&#xff0c;以“数据时代创新网管与信息安全”为口号&#xff0c;定位于终端安全、IT运营及企业服务化管理统一解决方案。 0x02 漏洞概述 Ruijie-EWEB 网管系统 flwo.control.ph…

MATLAB实战 | S函数的设计与应用

S函数用于开发新的Simulink通用功能模块&#xff0c;是一种对模块库进行扩展的工具。S函数可以采用MATLAB语言、C、C、FORTRAN、Ada等语言编写。在S函数中使用文本方式输入公式、方程&#xff0c;非常适合复杂动态系统的数学描述&#xff0c;并且在仿真过程中可以对仿真进行更精…

【代码】多种调度模式下的光储电站经济性最优 储能容量配置分析matlab/yalmip

程序名称&#xff1a;多种调度模式下的光储电站经济性最优储能容量配置分析 实现平台&#xff1a;matlab-yalmip-cplex/gurobi 代码简介&#xff1a;代码主要做的是一个光储电站经济最优储能容量配置的问题&#xff0c;对光储电站中储能的容量进行优化&#xff0c;以实现经济…

仿京东淘宝商品列表筛选组件:实现一个高效的侧边栏弹框筛选功能

仿京东淘宝商品列表筛选组件&#xff1a;实现一个高效的侧边栏弹框筛选功能 一、引言 随着电子商务的快速发展&#xff0c;用户体验成为了竞争的关键因素。在众多的电商网站中&#xff0c;如京东和淘宝&#xff0c;商品列表筛选功能为用户提供了便捷的途径来找到心仪的商品。本…

策略模式与简单工厂模式:终结if-else混乱,让代码更清爽

阅读建议 嗨&#xff0c;伙计&#xff01;刷到这篇文章咱们就是有缘人&#xff0c;在阅读这篇文章前我有一些建议&#xff1a; 本篇文章大概4500多字&#xff0c;预计阅读时间长需要5分钟。本篇文章的实战性、理论性较强&#xff0c;是一篇质量分数较高的技术干货文章&#x…

STM32-GPIO

一、GPIO简介 GPIO&#xff08;General Purpose Input Output&#xff09;通用输入输出口 可配置8种输入输出模式 引脚电平&#xff1a;0V~3.3V&#xff0c;部分引脚可容忍5V 输出模式下&#xff1a;可控制端口输出高低电平&#xff0c;用以驱动LED、控制蜂鸣器、模拟通信协议输…

Windows11系统下MemoryCompression导致内存占用率过高

. # &#x1f4d1;前言 本文主要是win11系统下CPU占用率过高如何下降的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#x1f304;每日…

java学习part30callabel和线程池方式

140-多线程-线程的创建方式3、4&#xff1a;实现Callable与线程池_哔哩哔哩_bilibili 1.Callable 实现类 使用方式 返回值 2.线程池

[英语学习][5][Word Power Made Easy]的精读与翻译优化

[序言] 今日完成第18页的阅读, 发现大量的翻译错误以及不准确. 需要分两篇文章进行讲解. [英文学习的目标] 提升自身的英语水平, 对日后编程技能的提升有很大帮助. 希望大家这次能学到东西, 同时加入我的社区讨论与交流英语相关的内容. [原著英文与翻译版对照][第18页] Wh…

Lattice-Based Blind Signatures: Short, Efficient, and Round-Optimal

目录 摘要引言 Lattice-Based Blind Signatures: Short, Efficient, and Round-Optimal CCS 2023 摘要 我们提出了一种基于随机预言机启发式和标准格问题&#xff08;环/模块SIS/LWE和NTRU&#xff09;的2轮盲签名协议&#xff0c;签名大小为22KB。该协议是全面优化的&#xf…

Elasticsearch 的使用

一、简介 1.Shard&#xff08;分片&#xff09; 数据分散集群的架构模式&#xff0c;Elasticsearch 将一个 Index&#xff08;索引&#xff09;中的数据切为多个 Shard&#xff08;分片&#xff09;&#xff0c;分布在不同服务器节点上。 默认每个索引会分配5个主分片和1个副本…

HarmonyOS开发工具安装

目录 下载与安装DevEco Studio DevEco Studio下载官网&#xff0c;点击下载 下载完成后&#xff0c;双击下载的“deveco-studio-xxxx.exe” 进入DevEco Studio安装向导 选择安装路径 如下安装选项界面勾选DevEco Studio后&#xff0c;单击“Next” 点击Install 安装完…

【云备份】业务处理

文章目录 1. 业务处理作用功能 2. 代码框架编写构造函数UpLoad ——文件上传请求ListShow —— 展示页面请求处理实现Download —— 下载请求的处理实现断点续传实现 1. 业务处理 作用 业务处理模块是对客户端的业务请求进行处理 功能 1.文件上传请求&#xff1a;备份客户端…

A--Z与a--z的ASCII码的差异

从z到A还有一些字符 应该改为str[i]>A&&str[i]<Z||str[i]>a&&str[i]<z;

什么是革命性技术eBPF?为什么可观测性领域都得用它

公众号「架构成长指南」&#xff0c;专注于生产实践、云原生、分布式系统、大数据技术分享。 如果有一种技术可以监控和采集任何应用信息&#xff0c;支持任何语言&#xff0c;并且应用完全无感知&#xff0c;零侵入&#xff0c;想想是不是很激动&#xff0c;那么这个技术是什么…

基于SpringBoot蜗牛兼职网的设计与实现

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;蜗牛兼职网当然也不能排除在外。蜗牛兼职网是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c…

【Element-ui】Layout与Container组件

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Layout 布局1.1 基础布局1.2 分栏间隔1.3 混合布局1.4 分栏偏移1.5 对齐方式1.6 响应式布局1.7 el-col中的 push和pull 二、Container 布局容器2.1 Contain…

c语言常见面试题(持续更新)

八股文的意义在于&#xff0c;如果你真正理解这些八股&#xff0c;那么你的编程语言才达到了入门级别&#xff0c;如果你不懂&#xff0c;你绝对还没有入门编程语言&#xff0c;也就是说在接下来的工作中&#xff0c;受限于基础的薄弱&#xff0c;你的工作进展会非常的慢&#…

Python函数的高级用法

Python 的函数是“一等公民”&#xff0c;因此函数本身也是一个对象&#xff0c;函数既可用于赋值&#xff0c;也可用作其他函数的参数&#xff0c;还可作为其他函数的返回值。 使用函数变量 Python 的函数也是一种值&#xff1a;所有函数都是 function 对象&#xff0c;这意…