vue+canvas实现移动端手写签名
作者:XingFang666 发布时间:2024-07-12 04:11:59
标签:vue,canvas,移动端,签名
本文实例为大家分享了vue+canvas实现移动端手写签名的具体代码,供大家参考,具体内容如下
<template>
<div class="sign">
<div class="header">
<i class="el-icon-arrow-left backImg" @click="goBack"></i>
<span class="title">个人签名</span>
</div>
<section class="signature">
<div class="signatureBox">
<div class="canvasBox" ref="canvasHW">
<canvas ref="canvasF" class="canvasStyle" @touchstart='touchStart' @touchmove='touchMove' @touchend='touchEnd' @mousedown="mouseDown" @mousemove="mouseMove" @mouseup="mouseUp"></canvas>
</div>
</div>
</section>
<div class="btnBox">
<div @click="overwrite" class="btn1">重置</div>
<div @click="commit" class="btn1">确定</div>
</div>
<div class="imglist-box" :style="imgUrlList.length>0 ? 'border: 1px solid #d9d9d9;' : ''">
<img v-for="i in imgUrlList" class="imgCanvas" :src="i">
<img v-show="imgUrlList.length>0" src="../../assets/img/signdelete.png" class="resign" @click="deleteAll">
</div>
<div class="tijiao-box">
<button @click="commitAll" class="tijiao">提 交</button>
</div>
</div>
</template>
<script>
import { Bus } from '@/utils'
export default {
name:'personsign',
data() {
return {
stageInfo:'',
imgUrl:'',
imgUrlList:[],
client: {},
points: [],
canvasTxt: null,
startX: 0,
startY: 0,
moveY: 0,
moveX: 0,
endY: 0,
endX: 0,
w: null,
h: null,
isDown: false,
isViewAutograph: this.$route.query.isViews > 0,
contractSuccess: this.$route.query.contractSuccess,
}
},
mounted() {
let canvas = this.$refs.canvasF
canvas.height = this.$refs.canvasHW.offsetHeight -0
canvas.width = this.$refs.canvasHW.offsetWidth - 0
this.canvasTxt = canvas.getContext('2d')
this.canvasTxt.lineWidth = 4
this.stageInfo = canvas.getBoundingClientRect()
},
methods: {
goBack(){
this.$router.go(-1)
// session.clear()
},
//mobile
touchStart(ev) {
ev = ev || event
ev.preventDefault()
if (ev.touches.length == 1) {
let obj = {
x: ev.targetTouches[0].clienX,
y: ev.targetTouches[0].clientY,
}
this.startX = obj.x
this.startY = obj.y
this.canvasTxt.beginPath()
this.canvasTxt.moveTo(this.startX, this.startY)
this.canvasTxt.lineTo(obj.x, obj.y)
this.canvasTxt.stroke()
this.canvasTxt.closePath()
this.points.push(obj)
}
},
touchMove(ev) {
ev = ev || event
ev.preventDefault()
if (ev.touches.length == 1) {
let obj = {
x: ev.targetTouches[0].clientX - this.stageInfo.left,
y: ev.targetTouches[0].clientY - this.stageInfo.top
}
this.moveY = obj.y
this.moveX = obj.x
this.canvasTxt.beginPath()
this.canvasTxt.moveTo(this.startX, this.startY)
this.canvasTxt.lineTo(obj.x, obj.y)
this.canvasTxt.stroke()
this.canvasTxt.closePath()
this.startY = obj.y
this.startX = obj.x
this.points.push(obj)
}
},
touchEnd(ev) {
ev = ev || event
ev.preventDefault()
if (ev.touches.length == 1) {
let obj = {
x: ev.targetTouches[0].clientX - this.stageInfo.left,
y: ev.targetTouches[0].clientY - this.stageInfo.top
}
this.canvasTxt.beginPath()
this.canvasTxt.moveTo(this.startX, this.startY)
this.canvasTxt.lineTo(obj.x, obj.y)
this.canvasTxt.stroke()
this.canvasTxt.closePath()
this.points.push(obj)
}
},
//pc
mouseDown(ev) {
ev = ev || event
ev.preventDefault()
if (1) {
let obj = {
x: ev.offsetX,
y: ev.offsetY
}
this.startX = obj.x
this.startY = obj.y
this.canvasTxt.beginPath()
this.canvasTxt.moveTo(this.startX, this.startY)
this.canvasTxt.lineTo(obj.x, obj.y)
this.canvasTxt.stroke()
// this.canvasTxt.strokeRect(20,20,80,100);
this.canvasTxt.closePath()
this.points.push(obj)
this.isDown = true
}
},
mouseMove(ev) {
ev = ev || event
ev.preventDefault()
if (this.isDown) {
let obj = {
x: ev.offsetX,
y: ev.offsetY
}
this.moveY = obj.y
this.moveX = obj.x
this.canvasTxt.beginPath()
this.canvasTxt.moveTo(this.startX, this.startY)
this.canvasTxt.lineTo(obj.x, obj.y)
this.canvasTxt.stroke()
this.canvasTxt.closePath()
this.startY = obj.y
this.startX = obj.x
this.points.push(obj)
}
},
mouseUp(ev) {
ev = ev || event
ev.preventDefault()
if (1) {
let obj = {
x: ev.offsetX,
y: ev.offsetY
}
this.canvasTxt.beginPath()
this.canvasTxt.moveTo(this.startX, this.startY)
this.canvasTxt.lineTo(obj.x, obj.y)
this.canvasTxt.stroke()
this.canvasTxt.closePath()
this.points.push(obj)
this.points.push({x: -1, y: -1})
this.isDown = false
}
},
//重写
overwrite() {
this.canvasTxt.clearRect(0, 0, this.$refs.canvasF.width, this.$refs.canvasF.height)
this.points = []
},
//确定签名
commit() {
this.imgUrl=this.$refs.canvasF.toDataURL();
this.imgUrlList.push(this.imgUrl)
if(this.imgUrlList.length>0){
this.canvasTxt.clearRect(0, 0, this.$refs.canvasF.width, this.$refs.canvasF.height)
this.points = []
}
},
deleteAll(){
this.imgUrlList = []
},
// 提交签名给前一页
commitAll(){
// 用canvas合并多张图片的base64为一张图的base64
var canvas = document.createElement("canvas");
canvas.width = 75*this.imgUrlList.length;
canvas.height = 100;
var context = canvas.getContext("2d");
context.rect(0 , 0 , canvas.width , canvas.height);
context.fillStyle = "#fff";
context.fill();
var myImage = new Image();
myImage.crossOrigin = 'Anonymous';
// 当签名列表有值时
if(this.imgUrlList.length>0){
for(let i = 0;i<this.imgUrlList.length;i++){
myImage.src = this.imgUrlList[i]
// 多张图片绘制成一张图片
context.drawImage(myImage , 50*i , 0 , 75 , 75); //context.drawImage(img,x,y,width,height);
// context.font = "60px Courier New";
// context.fillText("我是文字",350,450);
}
var base64 = canvas.toDataURL("image/jpg"); //"image/jpg" 这里注意一下
this.$router.go(-1) //要在bus之前写不然值传不回去
setTimeout(() => {
Bus.$emit('signImage',base64) //签名base64传给前一页
}, 300)
}
}
},
beforeDestroy(){
// 销毁bus
Bus.$off()
}
}
</script>
<style scoped lang="scss">
// 签名样式很重要,会影响触点位置
.sign{
width: 100%;
min-height: 100vh;
position: relative;
.header{
margin-bottom: 20px;
}
.tijiao-box{
width: 100%;
text-align: center;
}
.tijiao{
width: 90%;
height: 84px;
color: #fff;
border-radius: 2px;
background: #fa4b31;
box-shadow: 0 0 0px 1px #fa4b31;
font-size: 30px;
}
}
.signature{
width: 100%;
height: 50vh;
}
.imglist-box{
width: 90%;
margin: 0 auto;
margin-bottom: 20px;
position: relative;
}
.imgCanvas{
width: 150px;
height: 150px;
}
.resign{
width: 14%;
position: absolute;
top: 0;
right: 0;
}
.signatureBox {
width: 90%;
margin: 0 auto;
height: calc(100% - 50px);
box-sizing: border-box;
overflow: hidden;
background: #fff;
z-index: 100;
display: flex;
flex-direction: column;
align-items: center;
}
.canvasBox {
width: 100%;
align-items: center;
box-sizing: border-box;
flex: 1;
}
canvas {
background-image: url('../../assets/img/signbg.png');
background-position: center center;
background-repeat: no-repeat;
background-origin: border-box;
background-size: 100% 100%;
}
.btnBox{
width: 90%;
margin: 0 auto;
display: flex;
justify-content: space-between;
margin-bottom: 20px;
.btn1{
width: 46%;
height: 84px;
line-height: 84px;
color: #fa4b31;
border-radius: 2px;
background: #fff;
border: 1px solid #fa4b31;
box-shadow: 0 0 0px 1px #fa4b31;
font-size: 30px;
text-align: center;
}
}
.btnBox button:first-of-type {
background: transparent;
border-radius: 4px;
height: 40px;
width: 80px;
font-size: 14px;
}
.btnBox button:last-of-type {
background: #71b900;
color: #fff;
border-radius: 4px;
height: 40px;
width: 80px;
font-size: 14px;
}
</style>
重置就是清除田字格当前字,确定就将字保存为一张图片base64排列在列表。
重签就是删除列表所有图片,提交就是将多张图合并为一张且传给前一页显示。
来源:https://blog.csdn.net/xfmuchengxue/article/details/95175455


猜你喜欢
- 导入相关的包import pygame, sys, randomfrom pygame.locals import *设置屏幕大小以及基本参
- MySQL有多种存储引擎:MyISAM、InnoDB、MERGE、MEMORY(HEAP)、BDB(BerkeleyDB)、EXAMPLE、
- 1、层次索引1.1 定义在某一个方向拥有多个(两个及两个以上)索引级别,就叫做层次索引。通过层次化索引,pandas能够以较低维度形式处理高
- 本文实例讲述了PHP检查端口是否可以被绑定的方法。分享给大家供大家参考,具体如下:<?php/** * 检查端口是否可以被绑定 * @
- 独立 fmt Log输出重定向golang的fmt包的输出函数 Println、Printf、PrintStack等,默认将打印输出到os.
- 给明文密码加密的流程:import base64pwd_after_encrypt = base64.b64encode(b'thi
- 一、简介Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包
- 以如下代码为例,我们在局部作用域内使用全局变量a,需要使用global关键字进行声明。否则代码会不可用。a = 100def fun():&
- assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。断言可以在条件不满足程序运行的情况下直接返回错误,而不必
- 第一列按照goodsid局部分组,然后在分组后的记录中按照audittime升序排序得到序号,从而显示某商品得第几次变迁。 第二列是取该商品
- 在Python中,实现循环语句有以下几种方式:1. for 循环for 循环是 Python 中最常用的循环语句之一,可以遍历任何序列,如一
- 前言最近在工作中碰到一个小的排序问题,需要按嵌套对象的多个属性来排序,于是发现了Python里的operator模块和sorted函数组合可
- 一、了解字符编码的知识储备1. 文本编辑器存取文件的原理(nodepad++,pycharm,word) 打开编辑器就打开了启动了
- 有时候系统会以pytest的形式运行,如果不想以这种方式运行,换回普通模式,那么就点击画圈圈那里的白色三角行点击Edit configura
- 前言第一次尝试用Pyinstaller打包Pytorch,碰见了很多问题,耗费了许多时间!想把这个过程中碰到的问题与解决方法记录一下,方便后
- 以mysql-noinstall-5.0.22-win32为例,解压缩后会看到mysql-5.0.22-win32文件夹下面,有五个ini格
- 在一个项目中,制作呃echart图表的时候,遇到一个需求,需要从后端接口获取数据----售票员的姓名和业绩所以需要在订单表中,获取不同售票员
- 1、何为ansible-playbookplaybook是ansible用于配置,部署,和管理被控节点的剧本,通过playbook的详细描述
- 安 * oostpython调用C/C++的方法有很多,本文使用boost.python。考虑到后期有好多在boost上的开发工作,所以boo
- 问题描述我在flask程序中,启动了另一个python程序-test.py:os.system('nohup python /opt