uniapp: handwritten signature, multiple pictures combined into one picture

The content to be realized : handwritten signature, agreement content. After clicking submit: a picture is generated, with the content of the agreement, the date of signing and the signer.
The realized effect diagram is as follows:
insert image description here
insert image description here

1. Signature page

<template>
	<view class="index">
		<u-navbar title="电子协议" :is-back="false" :border-bottom="false" title-color="#333" :background="{background:''}">
		<view class="page_navbar_warp">
				<image src="../../static/icon/0.png" mode="" class="page_navbar_commonImg" @click="$go(1,1)"></image>
			</view>
		</u-navbar>
		<image src="https://www.*****/xieyi.png" mode="" class="banner"></image>
			<view class="signBox">
				<view class="title">签名区</view>
				<view style="width: 700rpx;height: 450rpx;">
					<l-signature disableScroll backgroundColor="rgba(255, 249, 238, .0)" ref="signatureRef" penColor="#333" :penSize="5" :openSmooth="true" ></l-signature>
				</view>
			</view>
		<view class="footer">
			<view class="btn1 t-c" @click="onClick('undo')">撤消</view>
			<u-button class="btn2 t-c" @click="onClick('save')" :loading="loading">提交</u-button>
		</view>
	</view>
</template>

<script>
	export default {
      
      
		data() {
      
      
			return {
      
      
				loading:false,
			}
		},
		methods:{
      
      
			onClick(type) {
      
      
				 if(type == 'openSmooth') {
      
      
					 this.openSmooth = !this.openSmooth
					 return
				 }
				if (type == 'save') {
      
      
					this.$refs.signatureRef.canvasToTempFilePath({
      
      
						success: (res) => {
      
      
							// 是否为空画板 无签名
							// 生成图片的临时路径
							// H5 生成的是base64
							let url = res.tempFilePath;
							console.log(res);
							if(res.isEmpty){
      
      
								this.$toast('请签名')
							}else{
      
      
								this.loading = true;
								this.$uploadImage('common/upload', url).then(res => {
      
      
									this.loading = false;
									if(res.code == 1){
      
      
										this.$go(2,'/pages/mine/canvas?signImg='+res.data.fullurl)
									}
								})
							}
						}
					})
					return
				}
				if (this.$refs.signatureRef) this.$refs.signatureRef[type]()
			}
		}
	}
</script>

<style scoped lang="scss">
	.index{
      
      
		min-height: 100vh;
		position: relative;
		.banner{
      
      
			display: block;
			width: 585rpx;
			height: 416rpx;
			margin: auto;
		}
		.signBox{
      
      
			border: 1rpx dashed #BF9350;
			width: 700rpx;
			height: 500rpx;
			margin: 32rpx auto;
			.title{
      
      
				padding-top: 32rpx;
				font-size: 40rpx;
				color: #BF9350;
				padding-left: 32rpx;
			}
		}
		.footer{
      
      
			position: fixed;
			left: 0;
			bottom: 0;
			width: 750rpx;
			height: 98rpx;
			background: #fff;
			box-shadow: 0rpx 3rpx 6rpx 1rpx rgba(0,0,0,0.32);
			padding: 0 50rpx;
			display: flex;
			align-items: center;
			justify-content: space-between;
			.btn1{
      
      
				width: 300rpx;
				height: 81rpx;
				border-radius: 41rpx 41rpx 41rpx 41rpx;
				border: 1rpx solid #BF9350;
				font-size: 32rpx;
				color: #BF9350;
			}
			.btn2{
      
      
				width: 300rpx;
				height: 81rpx;
				background: #BF9350;
				border-radius: 41rpx 41rpx 41rpx 41rpx;
				font-size: 32rpx;
				color: #fff;
			}
		}
	}
</style>

2. The canvas page is used to synthesize a picture

<template>
	<view class="demo">
		<u-navbar title="电子协议" :is-back="false" :border-bottom="false" title-color="#333" :background="{background:'#FFFAF3'}">
		<view class="page_navbar_warp">
				<image src="../../static/icon/0.png" mode="" class="page_navbar_commonImg" @click="$go(1,1)"></image>
			</view>
		</u-navbar>
		<canvas :style="{ width: canvasW + 'px', height: canvasH + 'px' }" canvas-id="myCanvas" id="myCanvas"></canvas>
		<view class="footer">
			<view class="btn1 t-c" @click="$go(1,1)">取消</view>
			<u-button class="btn2 t-c" @click="submit" :loading="loading" shape="circle" :ripple="true">提交</u-button>
		</view>
	</view>
</template>
<script>
	export default {
      
      
		components: {
      
      },
		data() {
      
      
			return {
      
      
				loading:false,
				canvasW:0, // 画布宽
				canvasH:0, // 画布高
				SystemInfo:{
      
      }, // 设备信息
				goodsImg: {
      
      }, // 协议图片
				signImg:{
      
      }, // 签名图片
				signW:120, // 签名图片大小
				bgImg:{
      
      },
				year:'',
				mon:'',
				date:'',
				tempFilePath:'',
			}
		},
		async onLoad(option) {
      
      
			var start = new Date();
			this.year = start.getFullYear();
			this.mon = start.getMonth() + 1;
			this.date = start.getDate();
			 // 获取设备信息,主要获取宽度,赋值给canvasW 也就是宽度:100%
			this.SystemInfo = await this.getSystemInfo();
			// 获取协议图片,签名二维码图片信息,APP端会返回图片的本地路径(H5端只能返回原路径)
			this.bgImg = await this.getImageInfo('https://www.*******/xieyi.png');
			this.goodsImg = await this.getImageInfo('https://www.*******/bg.png');
			this.signImg = await this.getImageInfo(option.signImg);
			this.canvasW = this.SystemInfo.windowWidth; // 画布宽度
			// #ifdef APP-PLUS
			this.canvasH = this.SystemInfo.windowHeight-94-uni.getSystemInfoSync().statusBarHeight;  // 画布高度 = 页面高度-(导航栏固定44px+footer的50px+APP内手机双跳栏的高度)
			// #endif
			// #ifdef H5
			this.canvasH = this.SystemInfo.windowHeight-94; 
			// #endif
			// 如果主图,二维码图片,设备信息都获取成功,开始绘制海报,这里需要用setTimeout延时绘制,否则可能会出现图片不显示。
			if(this.goodsImg.errMsg == 'getImageInfo:ok' && this.signImg.errMsg == 'getImageInfo:ok' && this.SystemInfo.errMsg == 'getSystemInfo:ok'){
      
      
				uni.showToast({
      
      
					icon:'loading',
					mask:true,
					duration:10000,
					title: '加载中,请稍后',
				});
				setTimeout(()=>{
      
      
					var ctx = uni.createCanvasContext('myCanvas', this);
					// 填充背景
					ctx.drawImage(this.bgImg.path, 0, 0, this.canvasW, this.canvasH) // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度)
					
					// 绘制协议主图
					ctx.drawImage(this.goodsImg.path, 50, 60, this.canvasW-100, this.canvasW-180) // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度)

					// 签署日期
					ctx.setFontSize(16)
					ctx.setFillStyle('#333')
					ctx.fillText(`签署日期:${ 
        this.year}${ 
        this.mon}${ 
        this.date}`, 50, this.canvasH -this.signW-80);
					
					// 签署人
					ctx.setFontSize(14)
					ctx.setFillStyle('#333')
					ctx.fillText('签署人:', 50, this.canvasH -this.signW-40);
					// 签署人
					ctx.drawImage(this.signImg.path, 90, this.canvasH-this.signW-80, this.signW, this.signW) // drawImage(图片路径,x,y,绘制图像的宽度,绘制图像的高度,二维码的宽,高)
					
					ctx.draw(true,(ret)=>{
      
       // draw方法 把以上内容画到 canvas 中。
						console.log(ret) 
						uni.showToast({
      
      
							icon:'success',
							mask:true,
							title: '绘制完成',
						});
						uni.canvasToTempFilePath({
      
       // 保存canvas为图片
							canvasId: 'myCanvas',
							quality: 1,
							complete: (res)=> {
      
      
								console.log(res)
								// 在H5平台下,tempFilePath 为 base64, // 图片提示跨域 H5保存base64失败,APP端正常输出临时路径
								if(res.tempFilePath){
      
      
									this.tempFilePath = res.tempFilePath;
								}
							},
						})
					});
				},1500)
			}else{
      
      
				console.log('err')
			}
		},
		methods: {
      
      
			submit(){
      
      
				this.loading = true;
				console.log('需要提交给后台的图片'this.tempFilePath)
			},
			// 获取图片信息
			getImageInfo(image) {
      
      
				return new Promise((req, rej) => {
      
      
					uni.getImageInfo({
      
      
						src: image,
						success: function(res) {
      
      
							req(res)
						},
					});
				})
			},
			
			// 获取设备信息
			getSystemInfo(){
      
      
				return new Promise((req, rej) => {
      
      
					uni.getSystemInfo({
      
      
					    success: function (res) {
      
      
					        req(res)
					    }
					});
				})
			},
		},
	}
</script>

<style scoped lang="scss">
	.footer{
      
      
		position: fixed;
		left: 0;
		bottom: 0;
		width: 750rpx;
		height: 50px;
		box-shadow: 0rpx 3rpx 6rpx 1rpx rgba(0,0,0,0.32);
		padding: 0 50rpx;
		display: flex;
		align-items: center;
		justify-content: space-between;
		background: #fff;
		.btn1{
      
      
			width: 300rpx;
			height: 40px;
			border-radius: 41rpx 41rpx 41rpx 41rpx;
			border: 1rpx solid #BF9350;
			font-size: 32rpx;
			color: #BF9350;
		}
		.btn2{
      
      
			width: 300rpx;
			height: 40px;
			background: #BF9350;
			border-radius: 41rpx 41rpx 41rpx 41rpx;
			font-size: 32rpx;
			color: #fff;
		}
	}
</style>

Remarks:
1. The agreement page is from l-signaturethe uniapp plug-in market.
2. The canvas page is inspired by a poster article
written before . 3. The xieyi.png (protocol content) and bg.png (bottom) used in the page Figure), and the signed option.signImg (signature image), both require background settings to allow cross-domain. Otherwise, H5 will report an error that the canvas is polluted and cannot generate base64.
insert image description here
This problem does not exist in APP, only H5 will appear.

Guess you like

Origin blog.csdn.net/qq_40745143/article/details/131957083