移动端合同等-用户签名,手机旋转签名,保存图片提交后台

    技术2025-02-16  20

    用到canvas技术+旋转transform: rotate(90deg)

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200704100343873.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Nzc3RtZw==,size_16,color_FFFFFF,t_70

    <!-- 显示签名 --> <!-- // `${process.env.VUE_APP_FILE_PATH}` // 文件返回地址 // process.env.VUE_APP_BASE_API // 上传与api的 '"http/"+imgUrl'--> <EmployeeSignature :date='date' :imgUrl='processurl + imgUrl' @changeSign="changeSign"></EmployeeSignature> // 详解 // imgUrl 默认 为imgUrl: false,为 图片地址 this.imgUrl = res.data.url // data 是日期 date: this.formattingDate(new Date()), //格式化日期 Vue.prototype.formattingDate = function (date) { var y = date.getFullYear(); var m = date.getMonth() + 1; m = m < 10 ? '0' + m : m; var d = date.getDate(); d = d < 10 ? ('0' + d) : d; return y + '-' + m + '-' + d; } // changeSign方法 changeSign() { this.signShow = true }, <!-- 签名组件 --> <Sign :signShow='signShow' @saveImg='saveImg' @close='signShow = false'></Sign> // signShow 是否显示 //保存签名 saveImg(imgUrl) { this.signShow = false this.imgUrl = imgUrl },

    // 组件1 第一张图的

    <template> <!-- 手写签名 --> <div @click="changeShow" class="EmployeeSignature"> <div class="topTitle"> <TopTitle type='online' color='#2f8cd8' title="员工签名区" /> </div> <span class="readtext">填表人确认已认真阅读上述特别说明并签字:</span> <p>日期:{{date}}</p> <div ref="EmployeeSignatureContent" class="EmployeeSignatureContent"> <div v-if="imgUrl==processurl?false:true"> <img class="imgurl" ref="imgUrl" :src="imgUrl" alt=""> </div> <div v-else> <img class="defalutIcon" style=" transform: rotate(0deg); -ms-transform: rotate(0deg);-moz-transform: rotate(0deg);-webkit-transform: rotate(0deg);-o-transform: rotate(0deg);height: 50px !important;width: 50px !important;margin-right: 10px;" src="@/assets/EmployeeSignature.png" alt=""> 点击手写签名 </div> <button @click.stop="clearImg">清空</button> </div> </div> </template> <script> import TopTitle from "@/components/topTitle.vue" export default { props: { imgUrl: { // 签完名以后获取的签名的图片 完整地址 type: Boolean | String, default: () => "" }, date: { type: String, default: () => { let date1 = new Date() var y = date1.getFullYear(); var m = date1.getMonth() + 1; m = m < 10 ? '0' + m : m; var d = date1.getDate(); d = d < 10 ? ('0' + d) : d; return `${y}-${m}-${d}`; } } }, watch: { imgUrl: { handler(newName, oldName) { if (newName != this.processurl) { setTimeout(() => { this.setImgUrl() }, 100) } }, immediate: true } }, components: { TopTitle }, data() { return { processurl: process.env.VUE_APP_FILE_PATH + 'false', } }, methods: { clearImg() { this.$emit('clearImg') }, setData(date) { var y = date.getFullYear(); var m = date.getMonth() + 1; m = m < 10 ? '0' + m : m; var d = date.getDate(); d = d < 10 ? ('0' + d) : d; return y + '-' + m + '-' + d; }, setImgUrl() { //动态设置图片大小 this.$nextTick(() => { this.$refs.imgUrl.style.width = this.$refs.EmployeeSignatureContent.clientHeight + 'px' this.$refs.imgUrl.style.height = this.$refs.EmployeeSignatureContent.clientWidth + 'px' }) }, changeShow() { this.$emit('changeSign') } }, } </script> <style lang='less' scoped> .EmployeeSignature { margin: 20px 0; .topTitle { margin: 0 5%; width: 90%; } p { margin: 0 5%; width: 90%; margin-top: 10px; text-align: right; } .readtext{ margin: 0 10%; width: 90%; margin-top: 10px; text-align: left; } .EmployeeSignatureContent { position: relative; display: flex; width: 90%; margin: 0 5%; align-items: center; justify-content: center; border: 1px dotted black; height: 130px; color: #5187ff; .imgurl { // height: 100%; // width: 100%; transform: rotate(270deg); -ms-transform: rotate(270deg); /* IE 9 */ -moz-transform: rotate(270deg); /* Firefox */ -webkit-transform: rotate(270deg); /* Safari 和 Chrome */ -o-transform: rotate(270deg); } button { height: 30px; width: 80px; display: flex; align-items: center; justify-content: center; font-size: 14px; color: #ffffff; background: #d7d7d7; position: absolute; right: 10px; bottom: 10px; border-radius: 15px; border: none; } } .defalutIcon { height: 50px; width: 50px; margin-right: 10px; } } </style>

    签名组件 第2张图

    <template> <!-- 签名组件 --> <van-overlay style="z-index:130" v-if="signShow" :show="signShow" @click="closeSignShow"> <div class="wrapper" @click.stop> <div class="toptitle"> 手写签名 </div> <div class="page sign-page"> <div class="content"> <div class="sign-wrap" id="signWrap"> <canvas id="myCanvas" style="height:100%;width:100%"></canvas> </div> </div> <div class="fix-btn"> <van-button size="large" type="hollow" class="hollow-primary-btn" @click.native="clearArea()">清除</van-button> <van-button size="large" type="primary" @click.native="saveSign()">提交</van-button> </div> </div> </div> </van-overlay> </template> <script> import axios from "axios" export default { props: { signShow: { // 是否显示 type: Boolean, default: () => false } }, data() { return { image: "", mousePressed: false, c: "", ctx: "", lastX: 0, lastY: 0, }; }, watch: { signShow: { handler(newName, oldName) { newName === true ? setTimeout(() => { this.newList() }, 100) : '' }, // 代表在wacth里声明了firstName这个方法之后立即先去执行handler方法 immediate: true } }, methods: { // 点击遮罩层关闭 closeSignShow(){ this.$emit('close') }, newList() { this.image = ""; this.mousePressed = false; var lastX, lastY; this.ctx = document.getElementById("myCanvas").getContext("2d"); this.c = document.getElementById("myCanvas"); var signWrap = document.getElementById("signWrap"); this.c.width = signWrap.clientWidth; //设置myCanvas宽度 this.c.height = signWrap.clientHeight; //设置myCanvas高度 //监听touchstart事件,touchmove事件,touchend事件等事件 this.InitThis(); }, saveImageInfo() { var image = this.c.toDataURL("image/png"); //得到生成后的签名base64位 url 地址 let params = { contract_number: this.$route.query.contract_number, img_base64: image //图片base6码 }; this.wxContractSignatureFun(params); }, InitThis() { // 触摸屏 var that = this; this.c.addEventListener( "touchstart", function (event) { if (event.targetTouches.length == 1) { event.preventDefault(); // 阻止浏览器默认事件,重要 var touch = event.targetTouches[0]; this.mousePressed = true; that.Draw( touch.pageX - this.offsetLeft, touch.pageY - this.offsetTop, false ); } }, false ); this.c.addEventListener( "touchmove", function (event) { if (event.targetTouches.length == 1) { event.preventDefault(); // 阻止浏览器默认事件,重要 var touch = event.targetTouches[0]; if (this.mousePressed) { that.Draw( touch.pageX - this.offsetLeft, touch.pageY - this.offsetTop, true ); } } }, false ); this.c.addEventListener( "touchend", function (event) { if (event.targetTouches.length == 1) { event.preventDefault(); // 阻止浏览器默认事件,防止手写的时候拖动屏幕,重要 // var touch = event.targetTouches[0]; this.mousePressed = false; } }, false ); // 鼠标 this.c.onmousedown = function (event) { this.mousePressed = true; that.Draw( event.pageX - this.offsetLeft, event.pageY - this.offsetTop, false ); }; this.c.onmousemove = function (event) { if (this.mousePressed) { that.Draw( event.pageX - this.offsetLeft, event.pageY - this.offsetTop, true ); } }; this.c.onmouseup = function (event) { this.mousePressed = false; }; }, Draw(x, y, isDown) { if (isDown) { this.ctx.beginPath(); this.ctx.strokeStyle = "#000"; //颜色 this.ctx.lineWidth = 3; //线宽 this.ctx.lineJoin = "round"; this.ctx.lineMax = 10; //设置画笔最大线宽 this.ctx.lineMin = 3; //设置画笔最小线宽 this.ctx.linePressure = 1.2; //设置画笔笔触压力 this.ctx.smoothness = 30; //设置画笔笔触大小变化的平滑度。 this.ctx.moveTo(this.lastX, this.lastY); this.ctx.lineTo(x, y); this.ctx.closePath(); this.ctx.stroke(); } this.lastX = x; this.lastY = y; }, clearArea() {//清空画板 this.ctx.setTransform(1, 0, 0, 1, 0, 0); this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); }, saveSign() { //提交签名 this.checkEmpty(); //调用 表单非空验证 }, checkEmpty() { var c = document.getElementById("myCanvas"); // 获取html的canvas对象,我这个id="myCanvas" if (this.isCanvasBlank(c)) { this.$toast.fail('请签名'); return; } else { var image = this.c.toDataURL("image/png"); //得到生成后的签名base64位 url 地址 image = image.replace(/^data:image\/\w+;base64,/, '') // replace消除前缀,获取完整的base64码 // process.env.VUE_APP_FILE_PATH // 文件返回地址 // process.env.VUE_APP_BASE_API // 上传与api的 axios.post(`${process.env.VUE_APP_FILEUP_API}` + 'api/upload', { dirName: 'public/personnel/mobile/signature', suffix: ".jpg", data: image }).then(res => { if (res.data.code == 200) { this.$toast.success('上传成功'); this.$emit('saveImg', res.data.data) } }) } }, //验证canvas画布是否为空函数 isCanvasBlank(canvas) { var blank = document.createElement("canvas"); //系统获取一个空canvas对象 blank.width = canvas.width; blank.height = canvas.height; return canvas.toDataURL() == blank.toDataURL(); //比较值相等则为空 }, } }; </script> <style lang="less" scoped> .wrapper { display: flex; align-items: center; justify-content: center; width: 90%; height: 90%; margin: 5%; background: red; } .page { height: 100%; width: 100%; background: skyblue; } .hollow-primary-btn { color: #228ade; border-color: #228ade; } .sign-page .content { font-size: 14px; height: 100%; width: 100%; color: #666; background: springgreen; } .sign-wrap { height: 100%; width: 100%; color: #666; background: springgreen; } #myCanvas { // width: 90%; // height: 150px; // border-radius: 10px; // margin: 10px 5% 5px 5%; color: #ff6000; background-color: #ffffff; } .fix-btn { position: fixed; bottom: 16%; display: flex; left: -19%; width: 200px; height: 100px; transform: rotate(90deg); -ms-transform: rotate(90deg); /* IE 9 */ -moz-transform: rotate(90deg); /* Firefox */ -webkit-transform: rotate(90deg); /* Safari 和 Chrome */ -o-transform: rotate(90deg); } .toptitle { position: fixed; top: 8%; display: -webkit-box; display: -webkit-flex; display: flex; right: 5%; /* width: 200px; */ /* height: 100px; */ transform: rotate(90deg); -ms-transform: rotate(90deg); -moz-transform: rotate(90deg); -webkit-transform: rotate(90deg); -o-transform: rotate(90deg); } </style>
    Processed: 0.011, SQL: 9