274 lines
8.9 KiB
Vue
274 lines
8.9 KiB
Vue
<template>
|
||
<canvas class="canvas" id="canvas" ref="canvas" v-bind="$attrs"></canvas>
|
||
</template>
|
||
|
||
<script>
|
||
import SignaturePad from 'signature_pad';
|
||
import { onMounted, ref } from '@vue/runtime-core';
|
||
import common from '@/api/common.js';
|
||
import { useRoute } from 'vue-router';
|
||
import { message } from 'ant-design-vue';
|
||
import { date } from 'is-type-of';
|
||
export default {
|
||
props: {
|
||
url: {
|
||
type: String,
|
||
default: () => ''
|
||
},
|
||
disabled: {
|
||
type: Boolean,
|
||
default: () => false
|
||
}
|
||
},
|
||
setup(props, content) {
|
||
// // 以数据 URL 的形式返回签名图像(请参阅 https://mdn.io/todataurl 了解可能的参数列表)
|
||
// signaturePad.toDataURL(); // save image as PNG
|
||
// signaturePad.toDataURL("image/jpeg"); // save image as JPEG
|
||
// signaturePad.toDataURL("image/svg+xml"); // save image as SVG
|
||
// // 从数据 URL 中绘制签名图像。
|
||
// // 注意:此方法不会填充表示绘制签名的内部数据结构。因此,使用#fromDataURL 后,#toData 将无法正常工作。
|
||
// signaturePad.fromDataURL("data:image/png;base64,iVBORw0K...");
|
||
// // 以点组数组的形式返回签名图像
|
||
// const data = signaturePad.toData();
|
||
// // 从点组数组中绘制签名图像
|
||
// signaturePad.fromData(data);
|
||
// // 从点组数组中绘制签名图像,而不清除现有图像(如果未提供,则清除默认为 true)
|
||
// signaturePad.fromData(data, { clear: false });
|
||
// // 清除画布
|
||
// signaturePad.clear();
|
||
// // 如果 canvas 为空,则返回 true,否则返回 false
|
||
// signaturePad.isEmpty();
|
||
// // 取消绑定所有事件处理程序
|
||
// signaturePad.off();
|
||
// // 重新绑定所有事件处理程序
|
||
const canvas = ref();
|
||
const signaturePad = ref();
|
||
const isErase = ref(false);
|
||
const imgList = ref([]);
|
||
const isSave = ref(false);
|
||
const options = ref();
|
||
const route = useRoute();
|
||
//用于画完图后的显示,在抬起时,添加来自于allPoints的所有点的数据
|
||
// const List<List> allList = new ArrayList<>();
|
||
onMounted(() => {
|
||
if (props.url) {
|
||
let image = new Image();
|
||
//解决跨域问题
|
||
image.crossOrigin = '*';
|
||
image.src = props.url + '?v=' + Math.random();
|
||
|
||
image.onload = () => {
|
||
var canvas = document.createElement('canvas');
|
||
|
||
canvas.width = 316;
|
||
canvas.height = 191;
|
||
var context = canvas.getContext('2d');
|
||
context.drawImage(image, 0, 0, 316, 191, 0, 0, 316, 191);
|
||
|
||
var quality = 1;
|
||
//这里的dataurl就是base64类型
|
||
var dataURL = canvas.toDataURL('image/jpg', quality); //使用toDataUrl将图片转换成jpeg的格式,不要把图片压缩成png,因为压缩成png后base64的字符串可能比不转换前的长!
|
||
signaturePad.value.fromDataURL(dataURL);
|
||
if (props.disabled) {
|
||
signaturePad.value.off();
|
||
}
|
||
};
|
||
}
|
||
|
||
// dotSize default = (minWidth + maxWidth/2)
|
||
// //number或函数(取返回值),作用为点击画布的时候,绘制一个半径为dotSize的圆
|
||
// //注意事项,从input 获取的值为string,而dotSize 为数值,如果通过input设置画笔大小,需要转number,不然就成了字符串拼接,就会出现超级大的点.
|
||
// minWidth default 0.5
|
||
// number画笔的笔锋
|
||
// maxWidth default 2.5
|
||
// number画笔的粗细
|
||
// throttle default 16
|
||
// number每秒绘制的次数,越大越耗性能,越细腻
|
||
// backgroundColor default null
|
||
// 画布背景色,可以为 green 字符串,#FFFFFF rgb(0,0,0)格式
|
||
// penColor default #000000
|
||
// 画笔颜色,可以为 green 字符串,#FFFFFF rgb(0,0,0)格式
|
||
// velocityFilterWeight default 0.7
|
||
// number 用于根据先前的速度修改新速度的重量
|
||
// onBegin
|
||
// 回调函数,落笔时候的回调
|
||
// onEnd
|
||
// 回调函数,收笔时候的回调
|
||
// const aa = new SignaturePad(canvas.value, {
|
||
// minWidth: 1,
|
||
// maxWidth: 1,
|
||
// onBegin: function() {
|
||
// console.log('sss')
|
||
// }
|
||
// });
|
||
signaturePad.value = new SignaturePad(canvas.value, {
|
||
minWidth: 2,
|
||
maxWidth: 2,
|
||
backgroundColor: '#fafafa'
|
||
});
|
||
signaturePad.value.width = 318;
|
||
signaturePad.value.height = 192;
|
||
signaturePad.value.addEventListener('beginStroke', (e) => {
|
||
content.emit('begin', e);
|
||
});
|
||
signaturePad.value.addEventListener('endStroke', (e) => {
|
||
content.emit('end', e);
|
||
console.log('end', e, signaturePad.value.toData());
|
||
});
|
||
if (props.disabled) {
|
||
signaturePad.value.off();
|
||
}
|
||
});
|
||
|
||
const base64ToFile = (dataUrl, name) => {
|
||
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], name + '.jpg', { type: 'image/jpg' });
|
||
};
|
||
|
||
//保存
|
||
const save = async () => {
|
||
if (signaturePad.value.isEmpty()) {
|
||
message.error('签名不能为空');
|
||
return;
|
||
}
|
||
let base64 = signaturePad.value.toDataURL();
|
||
let file = base64ToFile(base64, new Date().getTime());
|
||
const data = await common.cosUpload(file, file.name, '', () => {});
|
||
console.log(route.query.sn, file, data);
|
||
return data.url;
|
||
};
|
||
//清除
|
||
const clear = () => {
|
||
signaturePad.value.clear();
|
||
// setTimeout(() => {
|
||
// isErase.value = isErase2;
|
||
// erase();
|
||
// });
|
||
setTimeout(() => {
|
||
var ctx = canvas.value.getContext('2d');
|
||
ctx.globalCompositeOperation = 'source-over';
|
||
if (isErase.value) {
|
||
signaturePad.value.minWidth = 18;
|
||
signaturePad.value.maxWidth = 212;
|
||
signaturePad.value.penColor = '#fafafa';
|
||
signaturePad.value.velocityFilterWeight =
|
||
signaturePad.value.velocityFilterWeight + 0.1;
|
||
} else {
|
||
signaturePad.value.minWidth = 2;
|
||
signaturePad.value.maxWidth = 2;
|
||
signaturePad.value.penColor = '#000000';
|
||
signaturePad.value.velocityFilterWeight =
|
||
signaturePad.value.velocityFilterWeight + 0.1;
|
||
}
|
||
}, 100);
|
||
};
|
||
//撤回
|
||
const revoke = () => {
|
||
signaturePad.value.minWidth = 18;
|
||
signaturePad.value.maxWidth = 18;
|
||
const data = signaturePad.value.toData();
|
||
const newData = data.slice(0, data.length - 1);
|
||
signaturePad.value.fromData(newData, {
|
||
clear: true
|
||
});
|
||
// newData.forEach(async (s) => {
|
||
// await setTimeout(() => {
|
||
// signaturePad.value.fromData([s], {
|
||
// clear: false
|
||
// });
|
||
// });
|
||
// });
|
||
console.log(newData);
|
||
// console.log(newData);
|
||
// data = newData;
|
||
// signaturePad.value.fromData(newData, {
|
||
// clear: false
|
||
// });
|
||
|
||
setTimeout(() => {
|
||
var ctx = canvas.value.getContext('2d');
|
||
ctx.globalCompositeOperation = 'source-over';
|
||
if (isErase.value) {
|
||
signaturePad.value.minWidth = 18;
|
||
signaturePad.value.maxWidth = 18;
|
||
signaturePad.value.penColor = '#fafafa';
|
||
// ctx.globalCompositeOperation = 'destination-out';
|
||
} else {
|
||
signaturePad.value.minWidth = 2;
|
||
signaturePad.value.maxWidth = 2;
|
||
signaturePad.value.penColor = '#000000';
|
||
// ctx.globalCompositeOperation = 'source-over';
|
||
}
|
||
}, 100);
|
||
};
|
||
//擦除
|
||
const erase = () => {
|
||
console.log(isErase.value);
|
||
var ctx = canvas.value.getContext('2d');
|
||
ctx.globalCompositeOperation = 'source-over';
|
||
if (!isErase.value) {
|
||
signaturePad.value.minWidth = 18;
|
||
signaturePad.value.maxWidth = 18;
|
||
signaturePad.value.penColor = '#fafafa';
|
||
//ctx.globalCompositeOperation = 'destination-out';
|
||
isErase.value = true;
|
||
} else {
|
||
signaturePad.value.minWidth = 2;
|
||
signaturePad.value.maxWidth = 2;
|
||
signaturePad.value.penColor = '#000000';
|
||
|
||
isErase.value = false;
|
||
}
|
||
return isErase;
|
||
};
|
||
|
||
const onMousedown = (e) => {
|
||
console.log(e);
|
||
if (!isSave.value) {
|
||
isSave.value = true;
|
||
}
|
||
};
|
||
const onMouseleave = (e) => {
|
||
if (!isSave.value) {
|
||
isSave.value = false;
|
||
imgList.value.push(save());
|
||
}
|
||
};
|
||
const onMouserup = (e) => {
|
||
if (!isSave.value) {
|
||
isSave.value = false;
|
||
imgList.value.push(save());
|
||
}
|
||
};
|
||
|
||
return {
|
||
canvas,
|
||
signaturePad,
|
||
erase,
|
||
revoke,
|
||
clear,
|
||
save,
|
||
onMousedown,
|
||
onMouseleave,
|
||
onMouserup,
|
||
options
|
||
};
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style>
|
||
.canvas {
|
||
background: rgba(0, 0, 0, 0.02);
|
||
border-radius: 4px;
|
||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||
}
|
||
</style>
|