uniapp小程序自定义签名面板组件,小程序页面引用实现横屏签字(亲测有效)

这篇具有很好参考价值的文章主要介绍了uniapp小程序自定义签名面板组件,小程序页面引用实现横屏签字(亲测有效)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

需求: uniapp小程序自定义签字面板组件, canvas手写签名画板, 小程序页面引用实现横屏签字

实现效果:
uniapp签字组件,uni-app,小程序,前端,canva可画
uniapp签字组件,uni-app,小程序,前端,canva可画

一、自定义组件

uniapp签字组件,uni-app,小程序,前端,canva可画
在项目中创建components文件夹, 在文件夹下创建my-sign组件, 组件下创建my-sign.vue和index.js
my-sign.vue组件代码:

<template>
	<view class="signature-wrap">
		<canvas
			:canvas-id="cid"
			:id="cid"
			@touchstart="onTouchStart"
			@touchmove="onTouchMove"
			@touchend="onTouchEnd"
			disable-scroll
			:style="[
				{
					width: width && formatSize(width),
					height: height && formatSize(height)
				},
				customStyle
			]"
		></canvas>
		<slot />
	</view>
</template>

<script>
/**
 * sign canvas 手写签名
 * @description 设置线条宽度、颜色,撤回,清空
 * @tutorial
 * @property {String} cid canvas id 不设置则默认为 v-sign-时间戳
 * @property {String, Number} width canvas 宽度
 * @property {String, Number} height canvas 高度
 * @property {bgColor} bgColor 画布背景颜色
 * @property {Object} customStyle canvas 自定义样式
 * @property {String} lineWidth 画笔大小,权重小于 v-sign-pen 组件设置的画笔大小
 * @property {Number} lineColor 画笔颜色,权重小于 v-sign-pen 组件设置的画笔大小
 * @event {Function} init 当创建完 canvas 实例后触发,向外提供 canvas实例,撤回,清空方法
 * @example <v-sign @init="signInit"></v-sign>
 */
import { formatSize } from './index.js'

export default {
	name: 'my-sign',
	props: {
		// canvas id
		cid: {
			type: String,
			default: `v-sign-${Date.now()}`
			// required: true
		},
		// canvas 宽度
		width: {
			type: [String, Number]
		},
		// canvas 高度
		height: {
			type: [String, Number]
		},
		// 画笔大小,权重小于 v-sign-pen 组件设置的画笔大小 penLineWidth
		lineWidth: {
			type: Number,
			default: 4
		},
		// 线颜色,权重小于 v-sign-color 组件设置的画笔颜色 penLineColor
		lineColor: {
			type: String,
			default: '#333'
		},
		// 画布背景颜色
		bgColor: {
			type: String,
			default: '#fff'
		},
		// canvas自定义样式
		customStyle: {
			type: Object,
			default: () => ({})
		}
	},
	provide() {
		return {
			getSignInterface: this.provideSignInterface
		}
	},
	data() {
		return {
			formatSize,
			lineData: [],
			winWidth: 0,
			winHeight: 0,
			penLineWidth: null, // v-sign-pen 组件设置的画笔大小
			penLineColor: null // v-sign-color 组件设置的颜色
		}
	},
	created() {
		// 获取窗口宽高
		const { windowWidth, windowHeight } = uni.getSystemInfoSync()
		this.winWidth = windowWidth
		this.winHeight = windowHeight
	},
	mounted() {
		this.canvasCtx = uni.createCanvasContext(this.cid, this)
		// h5 需延迟绘制,否则绘制失败
		// #ifdef H5
		setTimeout(() => {
			// #endif
			this.setBackgroundColor(this.bgColor)
			// #ifdef H5
		}, 10)
		// #endif
		// 初始化完成,触发 init 事件
		this.$emit('init', this.provideSignInterface())
	},
	methods: {
		onTouchStart(e) {
			const pos = e.touches[0]
			this.lineData.push({
				style: {
					color: this.penLineColor || this.lineColor,
					width: this.penLineWidth || this.lineWidth
				},
				// 屏幕坐标
				coordinates: [
					{
						type: e.type,
						x: pos.x,
						y: pos.y
					}
				]
			})
			this.drawLine()
		},
		onTouchMove(e) {
			const pos = e.touches[0]
			this.lineData[this.lineData.length - 1].coordinates.push({
				type: e.type,
				x: pos.x,
				y: pos.y
			})
			this.drawLine()
		},
		onTouchEnd(e) {
			this.$emit('end', this.lineData)
		},
		// 清空画布
		clear() {
			this.lineData = []
			this.canvasCtx.clearRect(0, 0, this.winWidth, this.winHeight)
			this.canvasCtx.draw()
			this.setBackgroundColor(this.bgColor)
			this.$emit('clear')
		},
		// 撤销
		revoke() {
			this.setBackgroundColor(this.bgColor)
			this.lineData.pop()
			this.lineData.forEach((item, index) => {
				this.canvasCtx.beginPath()
				this.canvasCtx.setLineCap('round')
				this.canvasCtx.setStrokeStyle(item.style.color)
				this.canvasCtx.setLineWidth(item.style.width)
				if (item.coordinates.length < 2) {
					const pos = item.coordinates[0]
					this.canvasCtx.moveTo(pos.x, pos.y)
					this.canvasCtx.lineTo(pos.x + 1, pos.y)
				} else {
					item.coordinates.forEach(pos => {
						if (pos.type == 'touchstart') {
							this.canvasCtx.moveTo(pos.x, pos.y)
						} else {
							this.canvasCtx.lineTo(pos.x, pos.y)
						}
					})
				}
				this.canvasCtx.stroke()
			})
			this.canvasCtx.draw(true)
			this.$emit('revoke', this.lineData)
		},
		// 绘制线条
		drawLine() {
			const lineDataLen = this.lineData.length
			if (!lineDataLen) return
			const currentLineData = this.lineData[lineDataLen - 1]
			const coordinates = currentLineData.coordinates
			const coordinatesLen = coordinates.length
			if (!coordinatesLen) return
			let startPos
			let endPos
			if (coordinatesLen < 2) {
				// only start, no move event
				startPos = coordinates[coordinatesLen - 1]
				endPos = {
					x: startPos.x + 1,
					y: startPos.y
				}
			} else {
				startPos = coordinates[coordinatesLen - 2]
				endPos = coordinates[coordinatesLen - 1]
			}

			const style = currentLineData.style
			this.canvasCtx.beginPath()
			this.canvasCtx.setLineCap('round')
			this.canvasCtx.setStrokeStyle(style.color)
			this.canvasCtx.setLineWidth(style.width)
			this.canvasCtx.moveTo(startPos.x, startPos.y)
			this.canvasCtx.lineTo(endPos.x, endPos.y)
			// const P1 = this.caculateBezier(startPos, endPos, centerPos)
			// console.log(P1.x, P1.y)
			// this.canvasCtx.moveTo(startPos.x, startPos.y)
			// this.canvasCtx.quadraticCurveTo(P1.x, P1.y, endPos.x, endPos.y)
			this.canvasCtx.stroke()
			this.canvasCtx.draw(true)
		},
		// 保存png图片,文件名配置 filename 仅支持 h5
		async saveImage(filename = '签名') {
			const tempFilePath = await this.canvasToTempFilePath()
			return new Promise((resolve, reject) => {
				// #ifdef H5
				try {
					const a = document.createElement('a')
					a.href = tempFilePath
					a.download = filename
					document.body.appendChild(a)
					a.click()
					a.remove()
					resolve({
						errMsg: 'saveImageH5:ok'
					})
				} catch (e) {
					console.error(e)
					reject(e)
				}
				// #endif
				// #ifndef H5
				uni.saveImageToPhotosAlbum({
					filePath: tempFilePath,
					success(resObj) {
						resolve(resObj)
					},
					fail(err) {
						reject(err)
					}
				})
				// #endif
			})
		},
		// canvas 保存为临时图片路径,h5返回 base64
		canvasToTempFilePath(conf = {}) {
			return new Promise((resolve, reject) => {
				uni.canvasToTempFilePath(
					{
						canvasId: this.cid,
						...conf,
						success: res => {
							resolve(res.tempFilePath)
						},
						fail: err => {
							console.log('fail', err)
							reject(err)
						}
					},
					this
				)
			})
		},
		setBackgroundColor(color = '#fff') {
			this.canvasCtx.beginPath()
			this.canvasCtx.setFillStyle(color)
			this.canvasCtx.fillRect(0, 0, this.winWidth, this.winHeight)
			this.canvasCtx.fill()
			this.canvasCtx.draw(true)
		},
		setLineWidth(numberVal) {
			this.penLineWidth = numberVal
		},
		setLineColor(strValue) {
			this.penLineColor = strValue
		},
		// 向外暴露内部方法
		provideSignInterface() {
			return {
				cid: this.cid,
				ctx: this.canvasCtx,
				clear: this.clear,
				revoke: this.revoke,
				saveImage: this.saveImage,
				canvasToTempFilePath: this.canvasToTempFilePath,
				setLineWidth: this.setLineWidth,
				setLineColor: this.setLineColor,
				setBackgroundColor: this.setBackgroundColor,
				getLineData: () => this.lineData
			}
		},
		/**
		 * 计算二次贝塞尔曲线 控制点 P1
		 * 起点 P0(x0,y0)、控制点P1(x1, y1)、P2(x2, y2)、曲线上任意点B(x, y)
		 * 二次贝塞尔公式:B(t) = (1-t)²P0 + 2t(1-t)P1 + t²P2
		 * 代入坐标得:
		 * x = (1-t)²*x0 + 2t(1-t)*x1 + t²*x2
		 * y = (1-t)²*y0 + 2t(1-t)*y1 + t²*y2
		 */
		caculateBezier(P0, P2, B, t = 0.5) {
			const { x: x0, y: y0 } = P0
			const { x: x2, y: y2 } = P2
			const { x, y } = B
			let x1 = (x - (1 - t) * (1 - t) * x0 - t * t * x2) / (2 * t * (1 - t))
			let y1 = (y - (1 - t) * (1 - t) * y0 - t * t * y2) / (2 * t * (1 - t))
			return {
				x: x1,
				y: y1
			}
		}
	}
}
</script>

<style lang="scss" scoped>
.signature-wrap {
	position: relative;
}
</style>

index.js代码:

/**
 * 判断是否未数值
 * @param {Object} val
 */
export function isNumber(val) {
	return !isNaN(Number(val))
}

/**
 * 处理大小单位
 * @param {Object} val
 */
export function formatSize(val, unit = 'rpx') {
	return isNumber(val) ? `${val}${unit}` : val
}

二、配置小程序页面横屏

在pages.json中添加"pageOrientation": “landscape”, pageOrientation 设置为 landscape ,表示固定为横屏显示

uniapp签字组件,uni-app,小程序,前端,canva可画

{
	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
		{
			"path": "pages/index/index",
			"style": {
				"navigationBarTitleText": "签字",
				"enablePullDownRefresh": false,
				"pageOrientation": "landscape",
				"backgroundColor": "#f8f8f8",
				"navigationStyle": "custom"
			}
		}
	],
	"globalStyle": {
		"navigationBarTextStyle": "black",
		"navigationBarTitleText": "uni-app",
		"navigationBarBackgroundColor": "#F8F8F8",
		"backgroundColor": "#F8F8F8"
	},
	"uniIdRouter": {}
}

三、在页面中使用

uniapp签字组件,uni-app,小程序,前端,canva可画
uniapp签字组件,uni-app,小程序,前端,canva可画

代码:

<template>
	<view class="sign-contain">
		<view class="sign-top">
			请在空白处签字
		</view>
		<my-sign @init="onSignInit" @end="endConfirm" bgColor="#fff" width="100%" :height="signHeight">
		</my-sign>

		<!-- 按钮 -->
		<view class="signBtn-box">
			<view class="signBtn-item1">
				<button type="default" plain="true" class="lnvestor-btn" hover-class="hover"
					@click="cancelBtn">取消</button>
			</view>
			<view class="signBtn-item2">
				<button type="default" plain="true" class="lnvestor-btn1" hover-class="hover"
					@click="clear">清空重写</button>
				<button type="primary" class="lnvestor-btn2" hover-class="hover"
					@click="submitBtn" :disabled="vsignDisabled">提交签名</button>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				signHeight: '375px',
				vsignDisabled: true
			}
		},
		onLoad() {
			var that = this;
			uni.getSystemInfo({
				success: function(res) {
					console.log('屏幕信息', res)
					that.signHeight = (res.windowHeight-130)+"px";
				}
			})
		},
		methods: {
			submitBtn(){
				uni.redirectTo({
					url: '/qualifyLnvestor/qualifyLnvestor/result'
				})
			},
			// 取消
			cancelBtn(){
				uni.navigateBack({
					delta: 1
				})
			},
			// 清除
			clear() {
				this.signCtx.clear();
				this.vsignDisabled = true;
			},
			onSignInit(signCtx) {
				this.signCtx = signCtx
			},
			// 绘画结束触发
			endConfirm() {
				this.vsignDisabled = false;
			}
		}
	}
</script>

<style lang="scss">
	.sign-contain {
		padding-left: 35rpx;
		padding-right: 35rpx;

		.sign-top {
			width: 100%;
			height: 50px;
			line-height: 50px;
			font-size: 16px;
			text-align: center;
			color: #999999;
		}

		.signBtn-box {
			display: flex;
			justify-content: space-between;
			align-items: center;

			.signBtn-item1 {

				// 按钮样式
				.lnvestor-btn {
					margin-top: 11px;
					width: 94px;
					height: 40px;
					border-radius: 20px;
					display: flex;
					justify-content: center;
					align-items: center;
					font-size: 16px;
				}

				.hover {
					border: 1px solid #ccc !important;
					color: #ccc !important;
					font-size: 16px !important;
				}
			}

			.signBtn-item2 {
				display: flex;

				// 按钮样式
				.lnvestor-btn1 {
					margin-top: 11px;
					width: 128px;
					height: 40px;
					border-radius: 20px;
					display: flex;
					justify-content: center;
					align-items: center;
					font-size: 16px;
					margin-right: 16px;
				}

				.lnvestor-btn2 {
					margin-top: 11px;
					width: 128px;
					height: 40px;
					border-radius: 20px;
					display: flex;
					justify-content: center;
					align-items: center;
					background: #b99c65;
					font-size: 16px;
				}

				.hover {
					border: 1px solid #ccc !important;
					color: #ccc !important;
					font-size: 16px !important;
				}
			}
		}
	}
</style>

效果:
uniapp签字组件,uni-app,小程序,前端,canva可画文章来源地址https://www.toymoban.com/news/detail-753051.html

到了这里,关于uniapp小程序自定义签名面板组件,小程序页面引用实现横屏签字(亲测有效)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 记录一下:uniapp小程序分包后引用组件报错问题

     具体遇到的场景是这样的,我当前文件是在主包中,但是引入的这几个组件是在分包下面,于是就造成了引入错误,我曾尝试者用绝对跟相对路径引入缺仍然报错 最终看到一个贴记录的是: 小程序分包后,主包应该是不能引用分包的任何资源,分包可以引用主包的任何资源

    2024年02月15日
    浏览(43)
  • uniapp JS文件里面调用自定义组件(不用每个页面在template中加组件标签)

    前言 工具:uniapp ( Vue2 ) 开发端:微信小程序 其他:uview 2.0 场景:路由器里面,统一验证是否已登录,如果没登录,则直接弹出登录弹窗出来,不管哪个页面都可以直接弹出此组件不需每个页面去加入组件标签。 效果如下: 直接上代码: 第一步:组件封装 components目录下

    2024年02月16日
    浏览(49)
  • 小程序之修改引用的vant组件样式(包括自定义组件中的vant样式)

    今天在写小程序的时候,刚好遇到,以前遇到解决了之后忘记记录,今天记录下! 一般组件的基础样式会跟ui设计稿有些出入,就得改动! 使用vant的话,官方就有提供方法,比如复选框 我在项目中用custom-class居多,设置根节点后就能操作; 比如修改复选框禁用时的样式 如

    2024年02月11日
    浏览(61)
  • uniapp小程序中给web-view页面添加授权弹窗(使用cover-view组件覆盖实现该功能)

    效果图: web-view是承载网页的容器。会自动铺满整个小程序页面,个人类型的小程序暂不支持使用。 再看下面一个提示: 每个页面只能有一个 web-view,web-view 会自动铺满整个页面,并覆盖其他组件。 也就是说,小程序中使用web-view打开网页,在页面上写的其它组件会直接被

    2024年02月03日
    浏览(52)
  • uniapp中全局页面挂载组件(小程序)

    uniapp中页面全局挂载组件 首先我说的方法不是全局引入注册使用的时候把标签放在页面中 所需库 vue-inset-loader 步骤: 1.首先需要把uniapp项目 初始化 2.下载所需库 3.创建vue.config.js 文件 从HBuilder X创建的uniapp项目没有vue.config.js文件 所以需要建一个 这里面的配置我也是研究了好

    2023年04月09日
    浏览(45)
  • uniapp中全局页面挂载组件(小程序,h5)

    1.uniapp 自带的 easycom 使用easycom的好处 1、简化组件的使用,提高开发效率 2、不论组件目录下安装了多少组件,easycom打包后会自动剔除没有使用的组件,对组件库的使用尤为友好。 说明 easycom方式引入的组件 无需在页面内import ,也 不需要在components内声明 ,即可在任意页面

    2024年02月05日
    浏览(45)
  • uniapp 微信小程序:页面+组件的生命周期顺序

    这个uniapp的微信小程序项目使用的是 VUE2 首页只提供了一个跳转按钮。 虽然文档中将页面与组件的生命周期分开罗列,但是我们在页面和组件中所有的生命周期函数都加上,看下效果: uniap 页面生命周期 uniapp 组件生命周期

    2024年02月15日
    浏览(41)
  • uniapp写微信小程序实现电子签名

    写电子签名一定要注意的是一切全部按照手机上的适配来,为啥这么说呢,因为你在微信开发者工具中调试的时候认为是好的,正常的非常nice,当你发布版本的时候你会发现问题出来了。我下边的写法你可以直接用很简单。就是要记住canvas的几个属性和用法。 直接上干货 1.签

    2024年01月18日
    浏览(49)
  • uniapp小程序自定义扫码页面创建及其完善

    先上效果图: 同页面插入一个camera组件 其中应用到图片选择、cover-image、cover-view实现动画扫码功能。 不多说马上上代码! 参考链接camera | uni-app官网 (dcloud.net.cn) 2.utils.throttle 方法: 3.接收参数页面 以上主要参考http://t.csdn.cn/gsn1I中博主的分享,再进行个人更改去改善为自己

    2024年02月12日
    浏览(44)
  • 微信小程序子页面自定义tabbar组件

    有时候微信小程序会遇到代码合并,就比如把B小程序代码迁移到A小程序,要使得B作为A小程序的一个子页面子功能。因为本身小程序都有tabbar,原来B也有,这时候就要给B子功能自定义一个tabbar底部导航栏。(注意,这个不是微信小程序自定义tabBar,不需要app.json中设置一个

    2024年02月08日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包