<template>
<div class="position-fixed sign-page">
<div>簽名</div>
<div class="no-sign" @click="goSign">
<img :src="signature" v-if="signature" />
</div>
<!-- 畫布 -->
<div v-show="showCanvas" class="canvas-area position-fixed">
<span class="cancel rotate" v-show="showBtn" @click="handleCancel">取消 </span>
<span class="finish rotate" v-show="showBtn" @click="handleSave">完成</span>
<van-icon v-show="showBtn" name="revoke" size="24" color="#ff9900" class="revoke rotate" @click="handleRevoke" />
<canvas id="myCanvas"></canvas>
</div>
</div>
</template>
<script>
export default {
data() {
return {
canvas: null, // canvas對象
showCanvas: false, // 顯示畫布
showBtn: true, // 顯示畫布上的各種按鈕
ctx: null, // 畫筆
pointList: [], // 筆觸
cacheData: [], // 緩存每一步的圖像,用于回退
signature: '', // 簽名圖像
};
},
mounted() {
this.canvas = document.getElementById('myCanvas');
this.canvas.width = document.documentElement.clientWidth;
this.canvas.height = document.documentElement.clientHeight;
this.ctx = this.canvas.getContext('2d');
this.setBackground();
this.bindEvent();
},
methods: {
// 畫布背景設為白色
setBackground() {
this.ctx.fillStyle = '#ffffff';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
},
// 去設定簽名
goSign() {
this.showCanvas = true;
},
// 監聽觸摸事件
bindEvent() {
this.handleTouchStart();
this.handleTouchMove();
this.handleTouchEnd();
},
// 觸摸開始
handleTouchStart() {
const that = this;
this.canvas.ontouchstart = function (e) {
that.showBtn = false;
that.pointList = [];
that.drawSign(e);
};
},
// 觸摸移動
handleTouchMove() {
const that = this;
this.canvas.ontouchmove = function (e) {
that.drawSign(e);
};
},
// 繪制簽名
drawSign(e) {
const ctx = this.ctx;
e = e || event;
const x = e.touches[0].clientX;
const y = e.touches[0].clientY;
this.pointList.push([x, y]);
ctx.strokeStyle = '#000000';
ctx.beginPath();
ctx.lineWidth = 1;
this.pointList.forEach((p, idx) => {
if (idx === 0) {
ctx.moveTo(...p);
} else {
ctx.lineTo(...p);
}
});
ctx.stroke();
},
// 觸摸結束
handleTouchEnd() {
const that = this;
this.canvas.ontouchend = function () {
that.cacheData.push(that.ctx.getImageData(0, 0, that.canvas.width, that.canvas.height));
that.showBtn = true;
};
},
// 撤回
handleRevoke() {
if (this.cacheData.length > 1) {
this.cacheData.pop();
const img = this.cacheData[this.cacheData.length - 1];
this.ctx.putImageData(img, 0, 0);
} else if (this.cacheData.length === 1) {
this.cacheData.pop();
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.setBackground();
}
},
// 取消
handleCancel() {
this.showBtn = true;
this.pointList = [];
this.cacheData = [];
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.setBackground();
this.showCanvas = false;
},
// 儲存
handleSave() {
// 未繪制圖像
if (this.cacheData.length === 0) {
this.handleCancel();
return;
}
const data = this.canvas.toDataURL('image/png');
let img = new Image();
img.src = data;
const that = this;
img.onload = function () {
// 順時針旋轉 270 度
const width = that.canvas.width,
height = that.canvas.height;
that.canvas.width = height;
that.canvas.height = width;
that.ctx.rotate((Math.PI / 2) * 3);
that.ctx.drawImage(img, -width, 0);
that.signature = that.canvas.toDataURL('image/png');
// 還原寬高
that.canvas.width = width;
that.canvas.height = height;
that.handleCancel();
};
},
}
}
</script>
<style scoped >
.position-fixed {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 10;
}
.sign-page {
background: #ffffff;
padding: 12px 16px;
text-align: left;
.no-sign {
height: 160px;
background: #f2f2f2;
border-radius: 6px;
margin: 12px 0;
img {
width: 100%;
height: 160px;
background: #ffffff;
}
}
.canvas-area {
z-index: 14;
color: #ff9900;
background: #ffffff;
.cancel {
display: inline-block;
position: absolute;
right: 16px;
top: 20px;
}
.rotate {
transform: rotate(90deg);
-ms-transform: rotate(90deg);
-moz-transform: rotate(90deg);
-webkit-transform: rotate(90deg);
-o-transform: rotate(90deg);
}
.finish {
display: inline-block;
position: absolute;
right: 16px;
bottom: 20px;
}
.revoke {
position: absolute;
top: 50%;
left: 16px;
}
}
}
</style>