页面引入vue-esign
<template>
<div class="esigns">
<div class="rightText" @click="handleReset">重新书写</div>
<div style="width: 100%; margin: auto 0">
<cvue-esign
ref="esign"
class="cav"
:isCrop="isCrop"
:lineWidth="lineWidth"
:lineColor="lineColor"
:bgColor.sync="bgColor"
:height="cavHeight"
></cvue-esign>
</div>
</div>
</template>
<script>
// 下载依赖 npm i vue-esign --save
export default {
data() {
return {
lineWidth: 6,
lineColor: "#000000",
bgColor: "#F9FAF9",
resultImg: "",
isCrop: false,
cavHeight: 1280, // 画板高度
};
},
methods: {
handleReset() {
// 清除
this.$refs.esign.reset();
},
handleGenerate() {
_this.$refs.esign // 电子签名 需要转换成base64
},
},
};
</script>
<style scoped>
body {
position: fixed;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
background-color: rgb(51, 51, 51);
overflow: hidden;
}
#app {
width: 560px;
height: 320px;
}
@media screen and (orientation: portrait) {
#app {
position: absolute;
width: 100vh;
height: 100vw;
top: 0;
left: 100vw;
-webkit-transform: rotate(90deg);
-moz-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
transform-origin: 0% 0%;
}
}
@media screen and (orientation: landscape) {
#app {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
-webkit-transform: rotate(-90deg);
-moz-transform: rotate(-90deg);
-ms-transform: rotate(-90deg);
transform: rotate(-90deg);
transform-origin: 0% 0%;
}
}
.esigns {
height: 100%;
/*background-color: #ffffff;*/
}
.tip {
text-align: center;
line-height: 60px;
height: 60px;
font-size: 16px;
background-color: #fff;
/* color: cadetblue; */
font-weight: 500;
display: flex;
}
.btn {
/* display: flex;
justify-content: space-around;
margin-top: 10px; */
position: fixed;
left: 0;
bottom: 0;
width: 100%;
}
.cav {
width: 90%;
margin: auto;
border: aliceblue;
}
.foot-btn {
width: 45%;
background: -webkit-gradient(
linear,
right top,
left top,
from(#ff3d00),
to(#ff8c00)
);
background: -o-linear-gradient(right, #ff3d00 0%, #ff8c00 100%);
background: linear-gradient(270deg, #ff3d00 0%, #ff8c00 100%);
font-size: 0.36rem;
color: #fff;
height: 1rem;
line-height: 1rem;
}
.foot-btn-reset {
width: 45%;
/*background: -webkit-gradient(linear, right top, left top, from(#FF3D00), to(#ff8c00));*/
/*background: -o-linear-gradient(right, #FF3D00 0%, #ff8c00 100%);*/
/*background: linear-gradient(270deg, #FF3D00 0%, #ff8c00 100%);*/
font-size: 0.36rem;
color: #666666;
height: 1rem;
line-height: 1rem;
background-color: #ffffff;
}
.leftText{
margin-left: .4rem;
color: #1d1d1d;
}
</style>
vue-esign的页面书写
<template>
<canvas
ref="canvas"
@mousedown="mouseDown"
@mousemove="mouseMove"
@mouseup="mouseUp"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
></canvas>
</template>
<script>
export default {
name:'vue-esign',
props: {
width: {
type: Number,
default: 800,
},
height: {
type: Number,
default: 300,
},
lineWidth: {
type: Number,
default: 4,
},
lineColor: {
type: String,
default: "#000000",
},
bgColor: {
type: String,
default: "",
},
isCrop: {
type: Boolean,
default: false,
},
},
data() {
return {
hasDrew: false,
resultImg: "",
points: [],
canvasTxt: null,
startX: 0,
startY: 0,
isDrawing: false,
sratio: 1,
};
},
computed: {
ratio() {
return this.height / this.width;
},
stageInfo() {
return this.$refs.canvas.getBoundingClientRect();
},
myBg() {
return this.bgColor ? this.bgColor : "rgba(255, 255, 255)";
},
},
watch: {
myBg: function (newVal) {
this.$refs.canvas.style.background = newVal;
},
},
beforeMount() {
window.addEventListener("resize", this.$_resizeHandler);
},
beforeDestroy() {
window.removeEventListener("resize", this.$_resizeHandler);
},
mounted() {
const canvas = this.$refs.canvas;
canvas.height = this.height;
canvas.width = this.width;
canvas.style.background = this.myBg;
this.$_resizeHandler();
// 在画板以外松开鼠标后冻结画笔
document.onmouseup = () => {
this.isDrawing = false;
};
},
methods: {
$_resizeHandler() {
const canvas = this.$refs.canvas;
canvas.style.width = this.width + "px";
const realw = parseFloat(window.getComputedStyle(canvas).width);
canvas.style.height = this.ratio * realw + "px";
this.canvasTxt = canvas.getContext("2d");
this.canvasTxt.scale(1 * this.sratio, 1 * this.sratio);
this.sratio = realw / this.width;
this.canvasTxt.scale(1 / this.sratio, 1 / this.sratio);
},
// pc
mouseDown(e) {
e = e || event;
e.preventDefault();
this.isDrawing = true;
this.hasDrew = true;
let obj = {
x: e.offsetX,
y: e.offsetY,
};
this.drawStart(obj);
},
mouseMove(e) {
e = e || event;
e.preventDefault();
if (this.isDrawing) {
let obj = {
x: e.offsetX,
y: e.offsetY,
};
this.drawMove(obj);
}
},
mouseUp(e) {
e = e || event;
e.preventDefault();
let obj = {
x: e.offsetX,
y: e.offsetY,
};
this.drawEnd(obj);
this.isDrawing = false;
},
// mobile
touchStart(e) {
e = e || event;
e.preventDefault();
this.hasDrew = true;
if (e.touches.length === 1) {
let obj = {
x:
e.targetTouches[0].clientX -
this.$refs.canvas.getBoundingClientRect().left,
y:
e.targetTouches[0].clientY -
this.$refs.canvas.getBoundingClientRect().top,
};
this.drawStart(obj);
}
},
touchMove(e) {
e = e || event;
e.preventDefault();
if (e.touches.length === 1) {
let obj = {
x:
e.targetTouches[0].clientX -
this.$refs.canvas.getBoundingClientRect().left,
y:
e.targetTouches[0].clientY -
this.$refs.canvas.getBoundingClientRect().top,
};
this.drawMove(obj);
}
},
touchEnd(e) {
e = e || event;
e.preventDefault();
if (e.touches.length === 1) {
let obj = {
x:
e.targetTouches[0].clientX -
this.$refs.canvas.getBoundingClientRect().left,
y:
e.targetTouches[0].clientY -
this.$refs.canvas.getBoundingClientRect().top,
};
this.drawEnd(obj);
}
},
// 绘制
drawStart(obj) {
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.lineCap = "round";
this.canvasTxt.lineJoin = "round";
this.canvasTxt.lineWidth = this.lineWidth * this.sratio;
this.canvasTxt.stroke();
this.canvasTxt.closePath();
this.points.push(obj);
},
drawMove(obj) {
this.canvasTxt.beginPath();
this.canvasTxt.moveTo(this.startX, this.startY);
this.canvasTxt.lineTo(obj.x, obj.y);
this.canvasTxt.strokeStyle = this.lineColor;
this.canvasTxt.lineWidth = this.lineWidth * this.sratio;
this.canvasTxt.lineCap = "round";
this.canvasTxt.lineJoin = "round";
this.canvasTxt.stroke();
this.canvasTxt.closePath();
this.startY = obj.y;
this.startX = obj.x;
this.points.push(obj);
},
drawEnd(obj) {
this.canvasTxt.beginPath();
this.canvasTxt.moveTo(this.startX, this.startY);
this.canvasTxt.lineTo(obj.x, obj.y);
this.canvasTxt.lineCap = "round";
this.canvasTxt.lineJoin = "round";
this.canvasTxt.stroke();
this.canvasTxt.closePath();
this.points.push(obj);
this.points.push({ x: -1, y: -1 });
},
// 操作
generate() {
const pm = new Promise((resolve, reject) => {
if (!this.hasDrew) {
reject(`Warning: Not Signned!`);
return;
}
var resImgData = this.canvasTxt.getImageData(
0,
0,
this.$refs.canvas.width,
this.$refs.canvas.height
);
this.canvasTxt.globalCompositeOperation = "destination-over";
this.canvasTxt.fillStyle = this.myBg;
this.canvasTxt.fillRect(
0,
0,
this.$refs.canvas.width,
this.$refs.canvas.height
);
this.resultImg = this.$refs.canvas.toDataURL();
var resultImg = this.resultImg;
this.canvasTxt.clearRect(
0,
0,
this.$refs.canvas.width,
this.$refs.canvas.height
);
this.canvasTxt.putImageData(resImgData, 0, 0);
this.canvasTxt.globalCompositeOperation = "source-over";
if (this.isCrop) {
const crop_area = this.getCropArea(resImgData.data);
var crop_canvas = document.createElement("canvas");
const crop_ctx = crop_canvas.getContext("2d");
crop_canvas.width = crop_area[2] - crop_area[0];
crop_canvas.height = crop_area[3] - crop_area[1];
const crop_imgData = this.canvasTxt.getImageData(...crop_area);
crop_ctx.globalCompositeOperation = "destination-over";
crop_ctx.putImageData(crop_imgData, 0, 0);
crop_ctx.fillStyle = this.myBg;
crop_ctx.fillRect(0, 0, crop_canvas.width, crop_canvas.height);
resultImg = crop_canvas.toDataURL();
crop_canvas = null;
}
resolve(resultImg);
});
return pm;
},
reset() {
this.canvasTxt.clearRect(
0,
0,
this.$refs.canvas.width,
this.$refs.canvas.height
);
this.$emit("update:bgColor", "");
this.$refs.canvas.style.background = this.bgColor
? this.bgColor
: "rgba(255, 255, 255)";
this.points = [];
this.hasDrew = false;
this.resultImg = "";
},
getCropArea(imgData) {
var topX = this.$refs.canvas.width;
var btmX = 0;
var topY = this.$refs.canvas.height;
var btnY = 0;
for (var i = 0; i < this.$refs.canvas.width; i++) {
for (var j = 0; j < this.$refs.canvas.height; j++) {
var pos = (i + this.$refs.canvas.width * j) * 4;
if (
imgData[pos] > 0 ||
imgData[pos + 1] > 0 ||
imgData[pos + 2] ||
imgData[pos + 3] > 0
) {
btnY = Math.max(j, btnY);
btmX = Math.max(i, btmX);
topY = Math.min(j, topY);
topX = Math.min(i, topX);
}
}
}
topX++;
btmX++;
topY++;
btnY++;
const data = [topX, topY, btmX, btnY];
return data;
},
},
};
</script>
<style scoped>
canvas {
max-width: 100%;
display: block;
}
</style>