1.安装vue-esign插件
npm install vue-esign --save
2.引用
- 全局引用
import vueEsign from "vue-esign";
Vue.use(vueEsign);
- 局部引用
import vueEsign from "vue-esign"
components: {vueEsign}
3.使用
<template>
<div class="page sign-page">
<div class="back-arrow" @click="back">
<img class="arrow-left" src="../assets/images/arrow-right.png" alt="" />
</div>
<div class="sign-box">
<div
id="sign"
class="content"
style="background-color: white;padding-top:35px"
>
<vue-esign
v-if="platform != 'mobile'"
ref="esign"
:height="signHeight"
:width="375"
:isCrop="isCrop"
:lineWidth="lineWidth"
:lineColor="lineColor"
:bgColor.sync="bgColor"
/>
<vue-esign
v-else
ref="esign"
:height="signHeight"
:isCrop="isCrop"
:lineWidth="lineWidth"
:lineColor="lineColor"
:bgColor.sync="bgColor"
/>
</div>
<div class="con-btn" style="background-color: blue">
<span class="submit-btn size14" @click="handleGenerate()"
><span class="rotate-text">确认签名</span></span
>
<span class="staging-btn size14" @click="handleReset()"
><span class="rotate-text">清除签名</span>
</span>
</div>
</div>
</div>
</template>
<script>
import { photoUpload } from "../assets/js/getData";
import utils from "../assets/js";
import compress from "../assets/js/compress";
import html2canvas from "html2canvas";
export default {
name: "esignTest",
data() {
return {
platform: this.$platform,
lineWidth: 6,
lineColor: "#000000",
bgColor: "",
resultImg: "",
isCrop: false,
signHeight: document.body.clientHeight || 700,
};
},
methods: {
back() {
this.$emit("back");
},
handleReset() {
this.$refs.esign.reset();
},
getUserId() {
if (this.$route.params.id) {
return this.$route.params.id;
} else {
let dealdata = window.location.search.replace("?", "");
let item = [];
let result = "";
if (dealdata.split("=").length > 1) {
dealdata = dealdata.split("&");
dealdata.forEach((v) => {
item = v.split("=");
if (item[0] == "userId") {
result = item[1];
}
});
return result;
}
}
},
handleGenerate() {
this.$toast.loading({
message: "生成图片中...",
forbidClick: true,
});
this.$refs.esign
.generate()
.then((res) => {
this.handleImgRotate().then((r) => {
this.resultImg = r;
const blob = utils.convertBase64UrlToBlob(this.resultImg);
const file = utils.blobToFile(
blob,
`images${Math.floor(Math.random() * 100000)}`
);
if (file.size > 1048576) {
//图片太小了,压缩会变大,大于1M才压纹
// 压缩图片并上传
compress(
this.resultImg,
{
width: window.innerWidth,
height: window.innerHeight,
quality: 0.8,
},
(data) => {
this.uploadImg();
}
);
} else {
this.uploadImg();
}
});
})
.catch((err) => {
alert(err); // 画布没有签字时会执行这里 'Not Signned'
})
.finally(() => {});
},
// 上传图片
uploadImg() {
// 上传图片
const blobs = utils.convertBase64UrlToBlob(this.resultImg);
const files = utils.blobToFile(
blobs,
`images${Math.floor(Math.random() * 100000)}`
);
let params = new FormData();
params.append("file", files);
params.append("ticket", this.getUserId());
params.append("type", 0);
if (this.$route.params.id) {
params.append("ticketType", 0);
} else {
params.append("ticketType", 1);
}
this.$toast.loading({
message: "图片上传中...",
forbidClick: true,
});
// 上传
photoUpload(params, (data) => {
if (data.data) {
this.$emit("submitImg", this.resultImg, data.data.picId);
this.$emit("back");
this.$toast("上传成功");
this.$nextTick(() => {
this.$refs.esign.reset();
let img = document.getElementById("sign");
img.style.transform = "rotateZ(0deg)";
});
} else {
this.$toast.fail(data.msg);
let img = document.getElementById("sign");
img.style.transform = "rotateZ(0deg)";
}
});
},
// 处理图片旋转 方法
handleImgRotate() {
let img = document.getElementById("sign");
img.style.transform = "rotateZ(90deg)";
var w = parseInt(img.offsetWidth);
var h = parseInt(img.offsetHeight);
let _this = this;
var url;
return new Promise((resolve) => {
html2canvas(img).then((canvas) => {
if (canvas) {
canvas.style.width = w + "px";
canvas.style.height = h + "px";
url = canvas.toDataURL();
const img2 = new Image();
img2.src = url;
img2.style.transform = " scale(1.1)";
img2.onload = () => {
resolve(url);
};
}
});
});
},
},
};
</script>
<style scoped lang="less">
.page {
position: fixed;
width: 100%;
background-color: white;
top: 0px;
right: 0px;
z-index: 999;
.sign-box {
width: 100%;
display: flex;
}
.content {
width: 87%;
height: 100vh;
// background-color: wheat;
background-size: 100% 100%;
background-position: center center;
.sign-wrap {
width: 100%;
height: 100%;
}
}
.con-btn {
flex: 1;
display: flex;
flex-direction: column;
align-content: center;
justify-content: space-between;
opacity: 0.75;
height: 100vh;
span {
flex: 1;
text-align: center;
font-size: 18px;
width: 100%;
// height: 48px;
display: flex;
align-items: center;
justify-content: center;
width: 150%;
.rotate-text {
transform: rotateZ(-90deg);
padding-bottom: 23px;
font-size: 14px;
}
}
.staging-btn {
color: #007bf6;
background: #fff;
border-top: 1px solid #ebebeb;
}
.submit-btn {
color: #fff;
background: #007bf6;
}
}
}
.back-arrow {
position: fixed;
top: 10px;
left: 0px;
.arrow-left {
transform: rotateY(-180deg);
padding-right: 10px;
width: 15px;
height: 19px;
}
}
</style>
引用文件index.js
// base64 转blob
convertBase64UrlToBlob(dataurl) {
let arr = dataurl.split(",");
let mime = arr[0].match(/:(.*?);/)[1];
let bstr = atob(arr[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
},
// 将blob转成file
blobToFile(theBlob, fileName) {
theBlob.lastModifiedDate = new Date();
theBlob.name = fileName;
return theBlob;
},
formatTime,
};
export default utils;
引用文件compress.js
import utils from "../../assets/js";
function detectVerticalSquash(img) {
// 拍照在IOS7或以下的机型会出现照片被压扁的bug
var data;
var ih = img.naturalHeight;
var canvas = document.createElement("canvas");
canvas.width = 1;
canvas.height = ih;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
try {
data = ctx.getImageData(0, 0, 1, ih).data;
} catch (err) {
console.log("Cannot check verticalSquash: CORS?");
return 1;
}
var sy = 0;
var ey = ih;
var py = ih;
while (py > sy) {
var alpha = data[(py - 1) * 4 + 3];
if (alpha === 0) {
ey = py;
} else {
sy = py;
}
py = (ey + sy) >> 1; // py = parseInt((ey + sy) / 2)
}
var ratio = py / ih;
return ratio === 0 ? 1 : ratio;
}
/**
* dataURI to blob, ref to https://gist.github.com/fupslot/5015897
* @param dataURI
*/
function dataURItoBuffer(dataURI) {
var byteString = atob(dataURI.split(",")[1]);
var buffer = new ArrayBuffer(byteString.length);
var view = new Uint8Array(buffer);
for (var i = 0; i < byteString.length; i++) {
view[i] = byteString.charCodeAt(i);
}
return buffer;
}
function dataURItoBlob(dataURI) {
var mimeString = dataURI
.split(",")[0]
.split(":")[1]
.split(";")[0];
var buffer = dataURItoBuffer(dataURI);
return new Blob([buffer], { type: mimeString });
}
/**
* 获取图片的orientation
* ref to http://stackoverflow.com/questions/7584794/accessing-jpeg-exif-rotation-data-in-javascript-on-the-client-side
*/
function getOrientation(buffer) {
var view = new DataView(buffer);
if (view.getUint16(0, false) != 0xffd8) return -2;
var length = view.byteLength,
offset = 2;
while (offset < length) {
var marker = view.getUint16(offset, false);
offset += 2;
if (marker == 0xffe1) {
if (view.getUint32((offset += 2), false) != 0x45786966) return -1;
var little = view.getUint16((offset += 6), false) == 0x4949;
offset += view.getUint32(offset + 4, little);
var tags = view.getUint16(offset, little);
offset += 2;
for (var i = 0; i < tags; i++)
if (view.getUint16(offset + i * 12, little) == 0x0112)
return view.getUint16(offset + i * 12 + 8, little);
} else if ((marker & 0xff00) != 0xff00) break;
else offset += view.getUint16(offset, false);
}
return -1;
}
/**
* 修正拍照时图片的方向
* ref to http://stackoverflow.com/questions/19463126/how-to-draw-photo-with-correct-orientation-in-canvas-after-capture-photo-by-usin
*/
function orientationHelper(canvas, ctx, orientation) {
const w = canvas.width,
h = canvas.height;
if (orientation > 4) {
canvas.width = h;
canvas.height = w;
}
switch (orientation) {
case 2:
ctx.translate(w, 0);
ctx.scale(-1, 1);
break;
case 3:
ctx.translate(w, h);
ctx.rotate(Math.PI);
break;
case 4:
ctx.translate(0, h);
ctx.scale(1, -1);
break;
case 5:
ctx.rotate(0.5 * Math.PI);
ctx.scale(1, -1);
break;
case 6:
ctx.rotate(0.5 * Math.PI);
ctx.translate(0, -h);
break;
case 7:
ctx.rotate(0.5 * Math.PI);
ctx.translate(w, -h);
ctx.scale(-1, 1);
break;
case 8:
ctx.rotate(-0.5 * Math.PI);
ctx.translate(-w, 0);
break;
}
}
/**
* 压缩图片
*/
function compress(path, options, callback) {
const blobs = utils.convertBase64UrlToBlob(path);
const file = utils.blobToFile(
blobs,
`images${Math.floor(Math.random() * 1000000)}`
);
if (file.size < 800000) {
callback(path);
return;
}
const img = new Image();
img.src = path;
img.onload = function() {
const ratio = detectVerticalSquash(img);
const orientation = getOrientation(dataURItoBuffer(path));
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const maxW = options.width;
const maxH = options.height;
let w = img.width;
let h = img.height;
let dataURL;
if (w < h && h > maxH) {
w = parseInt((maxH * img.width) / img.height);
h = maxH;
} else if (w >= h && w > maxW) {
h = parseInt((maxW * img.height) / img.width);
w = maxW;
}
canvas.width = w * 2;
canvas.height = h * 2;
if (orientation > 0) {
orientationHelper(canvas, ctx, orientation);
}
ctx.drawImage(img, 0, 0, w * 2, (h * 2) / ratio);
dataURL = canvas.toDataURL("image/png", options.quality || 0.5);
if (/;base64,null/.test(dataURL) || /;base64,$/.test(dataURL)) {
// 压缩失败,以base64上传的,直接报错不上传
new Error("Compress fail, dataURL is " + dataURL + ".");
} else {
const blobNumber = utils.convertBase64UrlToBlob(path);
const files = utils.blobToFile(
blobNumber,
`images${Math.floor(Math.random() * 1000000)}`
);
if (files.size > 1000000) {
compress(dataURL, options);
} else {
callback(dataURL);
}
}
};
}
export default compress;