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

文章目录

    • 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. 图片进行翻转,返回翻转后的图片

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

阿里云通义千问720亿参数模型开源,适配企业级、科研级高性能应用

12月1日&#xff0c;阿里云举办通义千问发布会&#xff0c;开源通义千问720亿参数模型Qwen-72B。Qwen-72B在10个权威基准测评创下开源模型最优成绩&#xff0c;成为业界最强开源大模型&#xff0c;性能超越开源标杆Llama 2-70B和大部分商用闭源模型。未来&#xff0c;企业级、科…

windows系统如何配置yarn环境变量

启动前端项目&#xff0c;突然遇到报错&#xff1a; 原因在于没有安装yarn&#xff0c;或没有配置环境变量。 全局安装 yarn 可在vsCode中输入&#xff0c;也可在命令行输入&#xff08;winR&#xff0c;输入cmd&#xff09; npm install -g yarn添加环境变量 找到yarn的安…

scrapy爬虫中间件和下载中间件的使用

一、关于中间件 之前文章说过&#xff0c;scrapy有两种中间件&#xff1a;爬虫中间件和下载中间件&#xff0c;他们的作用时间和位置都不一样&#xff0c;具体区别如下&#xff1a; 爬虫中间件&#xff08;Spider Middleware&#xff09; 作用&#xff1a; 爬虫中间件主要负…

ChatGPT成为“帮凶”:生成虚假数据集支持未知科学假设

ChatGPT 自发布以来&#xff0c;就成为了大家的好帮手&#xff0c;学生党和打工人更是每天都离不开。 然而这次好帮手 ChatGPT 却帮过头了&#xff0c;莫名奇妙的成为了“帮凶”&#xff0c;一位研究人员利用 ChatGPT 创建了虚假的数据集&#xff0c;用来支持未知的科学假设。…

iOS简单理解区分MVC、MVP、MVVM

MVC、MVP、MVVM 前言 这篇文章简单介绍MVC、MVP和MVVM三种架构&#xff0c;并配上一个简单的Swift demo来区分MVC和MVVM两种架构。 MVC 传统MVC 下图是传统结构MVC&#xff0c;可以看到这种结构是紧耦合的&#xff0c;不推荐使用。 苹果的MVC 如下图&#xff0c;这是苹果…

智能安防无人机——一种安防巡检新方案

在高新技术的推动下&#xff0c;安防无人机在监控、巡逻等领域的使用频率越来越高&#xff0c;逐渐成为安防救援的重要帮手。安防无人机作为城市安全应急保障体系的重要组成部分&#xff0c;在未来将变得不可或缺。 一、安防无人机的定义及构成 复亚智能无人机全自主巡飞系统由…

【android开发-01】android中toast的用法介绍

1&#xff0c;android中toast的作用 在Android开发中&#xff0c;Toast是一种用于向用户显示简短消息的轻量级对话框。它通常用于向用户提供一些即时的反馈信息&#xff0c;例如操作结果、提示或警告。 Toast的主要作用如下&#xff1a; 提供反馈&#xff1a;Toast可以在用户…

wordpress安装之Linux ftp传输

工欲善其事,必先利其器。 最近准备在自己的服务器上搭建一个个人技术分享的平台。 因为我发现现在网络上的工具呀&#xff0c;还有一些问题的解答总是模棱两可&#xff0c;所以我打算自己做一个。 首先呢&#xff0c;我们需要有一个linxu的系统当服务器&#xff0c;然后呢&a…

LV.12 D21 PWM实验 学习笔记

一、PWD简介 1.1 蜂鸣器工作原理 有源蜂鸣器 有源蜂鸣器只要接上额定电源就可以发出声音 无源蜂鸣器 无源蜂鸣器利用电磁感应原理&#xff0c;为音圈接入交变电流后形成的电磁铁与永磁铁相吸或相斥而推动振膜发声 1.2 使用GPIO控制 while(1) { GPX2.DATGPX2.D…

certbot—30秒部署你的HTTPS,永久免费,自动续约

在之前我已经介绍过部署反向代理的2种方式了。第一种是通过宝塔的反向代理配置然后开启HTTPS。 第二种是通过nginxproxymanager。 今天要给大家分享的是一个 certbot。 Certbot 是一个由 Lets Encrypt 开发的免费开源工具&#xff0c;用于自动化部署和管理 SSL/TLS 证书。它具有…

16、什么是损失函数

上一节介绍了训练的过程,一个模型在训练的过程中,每一轮训练数据计算到到最后一层时,都会输出本轮的预测值,那么如何将本轮的预测值与标签中的真实值进行对比呢? 这就要用到损失函数(Loss function)。 什么是损失函数 损失函数是用来衡量模型预测结果与真实标签(grou…

linux 内核工作队列技术原理

首先介绍一下工作队列使用的术语。 work&#xff1a;工作&#xff0c;也称为工作项。work queue&#xff1a;工作队列&#xff0c;就是工作的集合&#xff0c; work queue 和 work 是一对多的关系。worker&#xff1a; 工人&#xff0c; 一个工人对应一个内核线程&#xff0c;…

MIAOYUN荣获“2023中国赛宝信息技术应用创新优秀解决方案应用创新示范方向三等奖”

11月30日&#xff0c;2023&#xff08;第四届&#xff09;数字化转型推动高质量发展大会在中国海口成功召开&#xff0c;会上举办了2023中国赛宝信息技术应用创新优秀解决方案征集活动颁奖仪式。成都元来云志科技有限公司&#xff08;简称“MIAOYUN”&#xff09;联合国网浙江省…

15、 深度学习之正向传播和反向传播

上一节介绍了训练和推理的概念,这一节接着训练和推理的概念讲一下,神经网络的正向传播和反向传播。 其实单看正向传播和反向传播这两个概念,很好理解。 正向传播(Forward Propagation)是指从输入层到输出层的数据流动过程,而反向传播(Backpropagation)是指数据从输出…

element ui el-date-picker日期时间选择器 设置只能选择不大于30天时间范围

需求&#xff1a;要求日期时间选择器只能选择最多32天&#xff0c;其他日期为不可点击状态。 日期组件type为daterange或者datetimerange都生效 实现&#xff08;vue2.x&#xff09;&#xff1a; 通过属性picker-options html <el-date-pickerv-model"dateTime&qu…

<Linux>(极简关键、省时省力)《Linux操作系统原理分析之存储管理(2)》(15)

[TOC](《Linux操作系统原理分析之存储管理&#xff08;2&#xff09;》&#xff08;15&#xff09; 5 存储管理5.4 分页存储管理5.4.1 纯分页存储管理a.页&#xff08;页面&#xff09;和物理块&#xff08;帧&#xff09;b. 页面大小c. 逻辑地址结构 5.5 存储扩充技术5.5.2 交…

spring cache 学习 —— @Cacheable 使用详解

1. 功能说明 Cacheable 注解在方法上&#xff0c;表示该方法的返回结果是可以缓存的。也就是说&#xff0c;该方法的返回结果会放在缓存中&#xff0c;以便于以后使用相同的参数调用该方法时&#xff0c;会返回缓存中的值&#xff0c;而不会实际执行该方法。 注意&#xff0c;这…

上门预约洗鞋店小程序

互联网洗鞋店小程序开发&#xff0c;结合洗鞋行业线下实际运营情况和经验&#xff0c;专为洗鞋人、洗鞋店打造的高效、实用、有价值的洗鞋私域流量管理软件系统。 帮助洗鞋人建立自己的私域流量&#xff0c;实现会员用户管理&#xff0c;用户与商家点对点互联互通&#xff0c;提…

okhttp导致的内存溢出(OOM)sun.security.ssl.SSLSocketImpl

使用分析工具&#xff1a;MAT(Memory Analyzer Tool)、JvisualVM占用内存&#xff1a;sun.security.ssl.SSLSocketImpl 一、 项目场景&#xff1a; 功能&#xff1a;一个定时任务(xxl-job)采用线程池的方式多线程请求第三方拉取数据&#xff0c;网络框架使用okhttp3。 问题&am…

从零开发短视频电商 在AWS上用SageMaker部署开源模型并用Java SDK调用

文章目录 1.创建AWS账户2.登录AWS3.创建域4.部署模型方式一 使用JumpStart可视化界面部署内置的模型方式二 采用python脚本部署私有模型5.调用模型AWS Java SDK调用Http调用6.监控7.自动扩缩容1.创建AWS账户 需要准备好邮箱一个,支持visa功能的信用卡一个。然后到aws上自己去…