微信小程序canvas实现简易手写签名版(uni-app)

微信小程序可以通过canvas实现手写签名的效果,本文中使用的是微信小程序Canvas 2D接口
本示例中绘制的是横屏签名的效果,效果图如下:
微信小程序canvas实现简易手写签名版(uni-app)_第1张图片
这里我们需要调整canvas的物理宽高,默认物理宽高为300*150px,物理宽高调整通过css样式即可,本文中需要根据屏幕高度进行动态调整,使用的是行内样式
页面布局:

<template>
	<view class="sign-page" :style="{paddingTop: top + 'px'}">
		<view class="canvas-box">
			<view class="left-pane">
				<view class="f28 text-gray6 left-text">请签字确认view>
				<view class="right-box">
					<view class="left-button" @click="clearContext">
						<text class="ic ic-delete text-gray6">text>
						<text class="f30 text-gray6 ml5">清空text>
					view>
					<button class="right-button" @click="confirm">完成button>
				view>
			view>
			
			<canvas
				class="canvas"
				disable-scroll 
				type="2d" 
				id="myCanvas"
				@touchstart="handleTouchstart"
				@touchmove="handleTouchmove"
				:style="{
					width: canvasWidth + 'px', 
					height: canvasHeight + 'px'}">
			canvas>
			
			<view class="right-pane">
				<view class="dis-flex back-button" @click="back">
					<text class="ic ic-left text-gray6">text>
					<text class="text-gray6 ml15">取消text>
				view>
				<view class="title">
					<text class="text text-gray6">{{title || '检测人员'}}text>
				view>
			view>
		view>
		<canvas
			class="canvas2"
			disable-scroll 
			type="2d" 
			id="myCanvas2"
			:style="{
				width: canvasHeight + 'px', 
				height: canvasWidth + 'px'}">
		canvas>
	view>
template>

js代码:canvas的物理宽高调整后,canvas的逻辑宽高也需要进行调整,默认逻辑宽高是300*150px,(小程序Canvas 2D接口支持修改逻辑宽高),具体参考本文中的initCanvas方法


<script>
	export default {
		data() {
			return {
				canvasWidth: 300,
				canvasHeight: 150,
				top: 0,
				canvas: null,
				title: ''
			}
		},
		onLoad() {
			const menuData = uni.getMenuButtonBoundingClientRect()
			uni.getSystemInfo({
			  success: (res) => {
			    let navPadding = menuData.top - res.statusBarHeight
			    // 顶部高度 = 状态栏高度 + 胶囊按钮行高度 + 胶囊按钮上下的padding
			    let navHeight = res.statusBarHeight + navPadding * 2 + menuData.height
			    // 设置canvas的物理宽高
			    this.canvasWidth = res.windowWidth - 100
				this.canvasHeight = res.windowHeight - navHeight - 20
				this.top = navHeight
			  }
			})
		},
		onReady() {
			this.initCanvas()
		},
		methods: {
			initCanvas() {
				uni.createSelectorQuery()
					.select('#myCanvas')
					.fields({ node: true, size: true })
					.exec((res) => {
						// 修改canvas的逻辑宽高
						// 如果不修改canvas的逻辑宽高,仅通过样式修改canvas的宽高,会导致绘图时比例不对,
						// 如将物理宽度改为600,但逻辑宽度还是300,假设画图时的起点x是100,那么实际看到的绘图起点是200
						const canvas = res[0].node
						this.canvas = canvas
						this.ctx = canvas.getContext('2d')
						// canvas.width = this.canvasWidth
						// canvas.height = this.canvasHeight
						// 注意:按照上面方式调整,虽然逻辑宽高和物理宽高保持一致了,但是会发现画出来的线会有锯齿不够清晰
						// 因为不同设备上物理像素与逻辑像素是不一致的
						// 因此canvas的逻辑宽高等于物理宽高分别*dpr
						const dpr = wx.getSystemInfoSync().pixelRatio
						canvas.width = this.canvasWidth * dpr
						canvas.height = this.canvasHeight * dpr
						// 假设dpr等于2,,那么canvas的物理宽度是600,逻辑宽度就是1200,
						// 假设画图时的起点x是100,那么实际看到的绘图起点是50,此时只需要将绘图内容进行等比例放大即可
						this.ctx.scale(dpr, dpr)
					})
			},
			handleTouchstart(e) {
				this.lineBegin(e.touches[0].x, e.touches[0].y)
			},
			handleTouchmove(e) {
				this.lineTo(e.touches[0].x, e.touches[0].y)
			},
			lineBegin(x, y) {
				this.ctx.beginPath()
				// 新版Canvas 2D接口,直接修改属性即可
				this.ctx.lineCap = 'round'
				this.ctx.lineWidth = 5
				this.startX = x
				this.startY = y
				this.ctx.moveTo(this.startX, this.startY)
			},
			lineTo(x, y) {
				this.ctx.lineTo(x, y)
				this.ctx.stroke()
				this.ctx.moveTo(x, y)
			},
			clearContext() {
				this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
			},
			confirm() {
				uni.canvasToTempFilePath({
					canvas: this.canvas, 
					success: (res) => {
						this.rotateImage(res.tempFilePath)
					}
				})
			},
			// 横屏签名,但是canvas的方向是垂直的,导出的图片也是竖屏,需要将图片进行旋转
			rotateImage(filePath) {
				uni.createSelectorQuery()
					.select('#myCanvas2')
					.fields({ node: true, size: true })
					.exec((res) => {
						// 首先绘制一个宽高与上面canvas相反的canvas
						const canvas = res[0].node
						this.canvas2 = canvas
						this.ctx2 = canvas.getContext('2d')
						
						const dpr = wx.getSystemInfoSync().pixelRatio
						canvas.width = this.canvasHeight * dpr
						canvas.height = this.canvasWidth * dpr
						this.ctx2.scale(dpr, dpr)
						// 绘制上述导出的签名图片到新的canvas上
						const img = this.canvas2.createImage()
						img.src = filePath
						img.onload = () => {
							// 签名图片旋转绘画解析在下方
							this.ctx2.translate(0, this.canvasWidth);
							this.ctx2.rotate(270 * Math.PI / 180)
							this.ctx2.drawImage(img, 0, 0, this.canvasWidth, this.canvasHeight)
							
							uni.canvasToTempFilePath({
								canvas: this.canvas2, 
								success: (res) => {
									this.handleUploadFile(res.tempFilePath)
								}
							}) 
						}						
					})
			},
			handleUploadFile(filePath) {
				uni.uploadFile({
		  			url: config.requestUrl + '/biz/file/upload/annex', 
			        filePath,
	  		        name: 'file',
					header: {
						'Authorization': getToken()
					},
				  	success: (res) => {
						// 调用接口成功
						if(res.statusCode == 200) {
							// 解析服务器返回数据
							const data = JSON.parse(res.data)
							if(data.code == 200) {
								const responseUrl = config.requestUrl + data.filePath
								const eventChannel = this.getOpenerEventChannel()
								eventChannel.emit('getSignImage', {filePath: responseUrl, fileId: data.fileId});
								this.back()
							}
						} else {
							uni.hideLoading()
						}
				  },
				  fail: (res) => {
				    uni.hideLoading()
				  }
				})
			},
			back() {
				uni.navigateBack()
			}
		}
	}
</script>	

由于签名的方向是横向的,但是canvas本身是竖向,导出的签名图片也为竖向,那么我们需要将导出的图片旋转为横向后重新绘制到canvas上然后导出。签名图旋转绘制的效果图如下:

图中黑色部分为canvas区域,竖向的签名图片为刚刚我们导出的图片,绘制到canvas上的效果如下图,如果我们要将其横向绘制到面板上,此时需要将绘画内容沿左上角顺时针旋转270deg,此时可以发现旋转后图片的覆盖区域在canvas向上移动canvasWdth的位置,那么旋转前将canvas的绘画起点重置到(0,canvasWidth)即可保证绘画内容旋转后刚好覆盖在canvas上
微信小程序canvas实现简易手写签名版(uni-app)_第2张图片
需要注意的点是,后面绘画旋转后的canvas在实际页面中我们是不需要看到的,需要通过样式将其隐藏,如果需要使用canvasToTempFilePath方法导出图片的话,不能使用display:none的隐藏canvas,否则会报错no image found,
可以通过定位和visiblity(opacity)属性隐藏。

页面样式:


你可能感兴趣的:(JS,微信小程序,小程序,前端)