1.首先先画签名的cavans
<template>
<div class="signature">
<!--touchstart,touchmove,touchend,touchcancel 这-->
<div class="signature__header">
<van-nav-bar
title="电子签名"
@click-left="onClickLeft"
>
<div slot="left">
<van-icon name="arrow-left" size="25px" color="#b6b6b6" />
</div>
</van-nav-bar>
</div>
<div class="signature__zang"></div>
<div>
<div>
<canvas id="canvas" :height="tabListHeight">Canvas画板</canvas>
</div>
<div class="signature__button">
<van-button type="primary"
size="small"
v-on:click="save">保存, 下一步</van-button>
<van-button type="default"
size="small"
v-on:click="clear">清除</van-button>
</div>
</div>
</div>
</template>
<script>
var draw
var preHandler = function (e) {
e.preventDefault()
}
class Draw {
constructor (el) {
this.el = el
this.canvas = document.getElementById(this.el)
this.canvas.width = document.documentElement.clientWidth
this.cxt = this.canvas.getContext('2d')
this.stage_info = this.canvas.getBoundingClientRect()
this.path = {
beginX: 0,
beginY: 0,
endX: 0,
endY: 0
}
}
init (btn) {
var that = this
this.canvas.addEventListener('touchstart', function (event) {
document.addEventListener('touchstart', preHandler, { passive: false })
that.drawBegin(event)
})
this.canvas.addEventListener('touchend', function (event) {
document.addEventListener('touchend', preHandler, { passive: false })
that.drawEnd()
})
this.clear(btn)
}
drawBegin (e) {
var that = this
window.getSelection()
? window.getSelection().removeAllRanges()
: document.selection.empty()
this.cxt.strokeStyle = '#000'
this.cxt.beginPath()
this.cxt.moveTo(
e.changedTouches[0].clientX - this.stage_info.left,
e.changedTouches[0].clientY - this.stage_info.top
)
this.path.beginX = e.changedTouches[0].clientX - this.stage_info.left
this.path.beginY = e.changedTouches[0].clientY - this.stage_info.top
this.canvas.addEventListener('touchmove', function () {
that.drawing(event)
})
}
drawing (e) {
this.cxt.lineTo(
e.changedTouches[0].clientX - this.stage_info.left,
e.changedTouches[0].clientY - this.stage_info.top
)
this.path.endX = e.changedTouches[0].clientX - this.stage_info.left
this.path.endY = e.changedTouches[0].clientY - this.stage_info.top
this.cxt.stroke()
}
drawEnd () {
document.removeEventListener('touchstart', preHandler, { passive: false })
document.removeEventListener('touchend', preHandler, { passive: false })
document.removeEventListener('touchmove', preHandler, { passive: false })
// canvas.ontouchmove = canvas.ontouchend = null
}
clear (btn) {
this.cxt.clearRect(0, 0, this.canvas.width, 600)
}
save () {
return this.canvas.toDataURL('image/png')
}
}
export default {
data () {
return {
msg: 'Welcome to Your Vue.js App',
val: true,
screenWidth: 0,
screenHeight: 0
}
},
mounted () {
draw = new Draw('canvas')
draw.init()
this.screenWidth = document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth
this.screenHeight = document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight
},
computed: {
tabListHeight () {
return this.screenHeight - 100
}
},
methods: {
onClickLeft () {
this.$router.go(-1)
},
clear: function () {
draw.clear()
},
save: function () {
var data = draw.save()
this.$router.push({ name: 'newidentity' })
sessionStorage.setItem('imgs', data)
console.log(111)
console.log(data)
},
mutate (word) {
this.$emit('input', word)
}
}
// data () {
// return {
// msg: 'Just use canvas to draw',
// degree: 0, // 屏幕整体旋转的角度, 可取 -90,90,180等值
// scope: [
// {
// value: 0,
// title: '正常',
// },
// {
// value: 90,
// title: '顺时针旋转90°',
// },
// {
// value: 180,
// title: '顺时针旋转180°',
// },
// {
// value: -90,
// title: '逆时针旋转90°',
// },
// ],
// };
// },
// components: {
// Draw,
// },
// mounted () {
// this.canvasBox = document.getElementById('canvasBox');
// this.initCanvas();
// },
// computed: {
// getHorizontalStyle () {
// console.log(232323)
// const d = document;
// const w = window.innerWidth || d.documentElement.clientWidth || d.body.clientWidth;
// const h = window.innerHeight || d.documentElement.clientHeight || d.body.clientHeight;
// let length = (h - w) / 2;
// let width = w;
// let height = h;
// switch (this.degree) {
// case -90:
// length = -length;
// case 90:
// width = h;
// height = w;
// break;
// default:
// length = 0;
// }
// if (this.canvasBox) {
// this.canvasBox.removeChild(document.querySelector('canvas'));
// this.canvasBox.appendChild(document.createElement('canvas'));
// setTimeout(() => {
// this.initCanvas();
// }, 200);
// }
// return {
// transform: `rotate(${this.degree}deg) translate(${length}px,${length}px)`,
// `${width}px`,
// height: `${height}px`,
// transformOrigin: 'center center',
// };
// },
// },
// methods: {
// initCanvas () {
// const canvas = document.querySelector('canvas');
// this.draw = new Draw(canvas, -this.degree);
// },
// clear () {
// this.draw.clear();
// },
// download () {
// this.draw.downloadPNGImage(this.draw.getPNGImage());
// },
// upload () {
// const image = this.draw.getPNGImage();
// const blob = this.draw.dataURLtoBlob(image);
// const url = '';
// const successCallback = (response) => {
// console.log(response);
// };
// const failureCallback = (error) => {
// return this.$toast(res.data.statusText);
// };
// this.draw.upload(blob, url, successCallback, failureCallback);
// },
// },
}
</script>
<style lang="less" scoped>
h1,
h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
#canvasBox {
display: flex;
flex-direction: column;
height: 100%;
}
canvas {
flex: 1;
cursor: crosshair;
background-color: #f2f2f2;
}
.signature {
height: 100%;
100%;
background-color: #ffffff;
position: relative;
.signature__header {
100%;
height: 46px;
position: fixed;
top: 0;
left: 0;
}
.signature__zang {
100%;
height: 46px;
}
.signature__button {
height: 50px;
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
}
}
#keyword-box {
margin: 10px 0;
}
</style>
2.合成
<template> <div id="identity" class="identity"> <div class="identity__header"> <van-nav-bar title="签订合同" @click-left="onClickLeft" > <div slot="left"> <van-icon name="arrow-left" size="25px" color="#b6b6b6" /> </div> </van-nav-bar> </div> <div class="identity__zang"></div> <div class="identity__content" :style="{ height: tabListHeight }"> <div class="identity__content-item"> <div class="identity-bnt van-hairline--top"> <!-- <div class="upphoto"> 上传交款凭证</div> --> <!-- 上传图片 --> <!-- <div class="pushphoto"> <img class="pushphoto-add" v-for="(item,index) of photo" :key="index" :src="item" @click="showimg(item)"> <van-uploader class="pushphoto-add" :after-read="onRead" v-if="falses"> <img class="pushphoto-add" src="../../assets/img/addphoto.png"> </van-uploader> </div> --> <!-- <div class="upphoto"> 上传身份证</div> <div class="pushphoto"> <img class="pushphoto-add" v-for="(item,index) of photo1" :key="index" :src="item" @click="showimg1(item)"> <van-uploader class="pushphoto-add" :after-read="onRead1" v-if="falses1"> <img class="pushphoto-add" src="../../assets/img/addphoto.png"> </van-uploader> </div> --> <div class="info-conter" ref="infoconter">
//这里是你的合同 <img src="../../assets/img/hetong.png" id="imgCover" ref="imgCover">
//这里是你的签名 <img v-show="zou" :src="ba64" id="imgUploadX"> <canvas v-show="yincang" id="can"></canvas> </div> <!-- <van-button type="primary" @click="pull">签订合同</van-button> --> </div> </div> </div> </div> </template> <script> export default { mounted () { this.$nextTick(() => { this.ba64 = sessionStorage.getItem('imgs'); //this.pull(); this.$toast.loading({ mask: true, message: '合同签名中,请等待...' });
//渲染需要一个过程,需要用到定时器,不然手机合成的签名合同是一片黑 setTimeout(()=>{ // console.log(Timer); this.pull(); },4000); this.screenWidth = document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth this.screenHeight = document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight }) }, computed: { tabListHeight () { return this.screenHeight / (this.screenWidth / 10) - 2 - 1.226667 + 'rem' } }, data () { return { ba64: '', zou: false, yincang: false, enen: false, frist: '', second: '', files: '', three: '', dataurls: '', threeimg: '', secondimg: '', fristimg: '', text1: true, text2: true, text3: true, imgs1: false, imgs2: false, imgs3: false, screenWidth: 0, screenHeight: 0, falses: true, falses1:true, photo: [], photo1: [], photolist: [], photolist1: [], } }, methods: { // onRead (i) { // this.photo.push(i.content); // //console.log(this.photo); // this.photolist.push(i.file) // if (this.photo.length >= 3) { // this.falses = false // } // console.log(this.photo) // }, // onRead1 (i) { // this.photo1.push(i.content) // this.photolist1.push(i.file) // if (this.photo1.length >= 3) { // this.falses1 = false // } // console.log(this.photo1) // }, // showimg (item) { // this.show = true // this.file = item // }, // showimg1 (item) { // this.show = true // this.file1 = item // }, // 合成图片 pull () { console.log(this.photolist); //console.log(this.photolist1); this.compound(); // setTimeout(()=>{ this.$router.push({ path: '/newesignature' }) // },5000); },
//这里是签名与合同的渲染合成 compound () { this.yincang = true let eleImgUploadX = document.getElementById('imgUploadX') let eleImgCover = document.getElementById('imgCover') let can = document.getElementById('can') let context = can.getContext('2d') let width = this.$refs.infoconter.offsetWidth let heigth = this.$refs.infoconter.offsetHeight let heigths = this.$refs.imgCover.offsetHeight let hei = this.$refs.imgCover.offsetHeight can.width = width can.height = heigths var devicePixelRatio = window.devicePixelRatio || 1, backingStoreRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1, ratio = devicePixelRatio / backingStoreRatio var oldWidth = can.width; var oldHeight = can.height; can.width = oldWidth * ratio can.height = oldHeight * ratio can.style.width = oldWidth + 'px' can.style.height = oldHeight + 'px' context.scale(ratio, ratio) console.log(width) context.drawImage(eleImgCover, 0, 0, width, heigth) context.drawImage(eleImgUploadX, width * 0.65, hei * 0.8, 95, 80) console.log(can.toDataURL('image/png')) let dataurls = can.toDataURL('image/jpeg') this.dataurls = can.toDataURL('image/jpeg') this.enen = true // 转文件flie let filename = 'canvas.jpg' let feli = dataURLtoFile(dataurls, filename) function dataURLtoFile (dataurl, filename) {//将base64转换为文件 var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n) while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new File([u8arr], filename, { type: mime }) } this.files = feli console.log(this.files) sessionStorage.setItem('files', this.dataurls) }, onClickLeft () { this.$router.push({ name: 'openvip' }) } } } </script> <style lang="less" scoped> .identity { 100%; // height: 100%; overflow: hidden; position: relative; img { 100%; height: 100%; } .identity__header { 100%; height: 46px; position: fixed; top: 0; left: 0; z-index: 999; } .identity__zang { 100%; height: 46px; } .info-conter { 100%; // height: 100%; // background-color: white; // overflow: hidden; overflow-x: auto; overflow-y: auto; position: absolute; right: 0; // bottom: -100%; } #imgCover { opacity: 0; } .info-conter img { 100%; } .identity__content{ 100%; background-color: #ffffff; overflow: scroll; -webkit-overflow-scrolling: touch; .identity__content-item { // height: 100%; 100%; .identity-frist { 100%; height: 180px; background-color: #f2f2f2; margin-top: 10px; margin-bottom: 10px; text-align: center; line-height: 180px; font-size: 18px; .van-uploader { 100%; height: 100%; } } } } .pushphoto { 100%; height: 100px; padding-left: 16px; padding-bottom: 16px; // margin-bottom: 20px; box-sizing: border-box; display: flex; .pushphoto-add { 25%; height: 100%; margin-right: 10px; img { 100%; } } } .identity-bottom { 100%; height: 118px; } .identity-bnt { 100%; // position: fixed; // bottom: 0px; // z-index: 99999; padding: 20px 20px; background-color: #ffffff; box-sizing: border-box; // height: 118px; .identity-reset { margin-bottom: 10px; } .van-button { 100%; } } } </style>
效果: