创建组件
<template>
<view class="box" v-show="modelValue" :style="{height:'100%'}">
<view class="whole canvas-autograph flexc">
<canvas class="scroll-view" canvas-id="mycanvas" @touchstart="touchstart" @touchmove="touchmove"
disable-scroll="true" @touchend="touchend" />
<view class="fun-box ">
<van-button round block type="warning" size="small" @click="clear">
清空
</van-button>
<van-button round block type="primary" size="small" @click="confirm">
确认
</van-button>
<van-button round block type="danger" size="small" @click="cancel">
取消
</van-button>
</view>
</view>
</view>
</template>
<script setup>
// base64转Blob格式方法
// base64转文件流方法
import {
parseBlob,
base64toFile,
blobToDataURI
} from '@/utils/base64ToFile.js'
// 使用七牛云封装的图片上传方法
import {
qiniuUploadSubscribe
} from '@/utils/upload.js'
import {
ref,
reactive,
watch,
getCurrentInstance,
onMounted
} from 'vue'
const {
proxy: t
} = getCurrentInstance()
const emits = defineEmits(['update:modelValue', 'complete'])
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
infor: {
type: Object,
default: {}
},
})
watch(() => props.modelValue, e => {}, {
immediate: true,
})
let cavWidth = ref(2000)
let cavWidth1 = ref(2000)
let points = reactive([])
let pointList = ref([])
const isDraw = ref(false)
let canvaCtx = reactive(uni.createCanvasContext('mycanvas'))
canvaCtx.lineWidth = 4;
canvaCtx.lineCap = 'round'
canvaCtx.lineJoin = 'round'
const touchstart = e => {
let startX = e.changedTouches[0].x
let startY = e.changedTouches[0].y
let startPoint = {
X: startX,
Y: startY
}
points.push(startPoint);
canvaCtx.beginPath();
}
const touchmove = e => {
let moveX = e.changedTouches[0].x
let moveY = e.changedTouches[0].y
let movePoint = {
X: moveX,
Y: moveY
}
points.push(movePoint)
pointList.value.push(movePoint)
let len = points.length
if (len >= 2) {
draw()
}
}
const draw = () => {
let point1 = points[0]
let point2 = points[1]
points.shift()
canvaCtx.moveTo(point1.X, point1.Y)
canvaCtx.lineTo(point2.X, point2.Y)
canvaCtx.stroke()
canvaCtx.draw(true)
}
const touchend = e => {
points = [];
}
const clear = () => {
pointList.value = []
return uni.getSystemInfo()
.then(res => {
canvaCtx.clearRect(0, 0, res.windowWidth, res.windowHeight);
canvaCtx.draw(true);
return res
})
.catch(err => {
console.log(err);
})
}
const confirm = () => {
if (pointList.value.length < 10) {
uni.showToast({
icon: 'none',
title: '请签字'
})
} else {
uni.canvasToTempFilePath({
canvasId: 'mycanvas',
})
.then(res => {
const {
tempFilePath
} = res
getImageInfo(tempFilePath)
})
}
}
const cancel = () => {
pointList.value = []
clear().then((res) => {
emits('update:modelValue', false);
})
}
const getImageInfo = (src) => {
const {
infor: {
idCard
}
} = props
let lista = {}
uni.showLoading({
title: "正在上传"
})
uni.getImageInfo({
src,
success(res) {
let img = new Image()
img.src = res.path
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d')
let rate = res.height / res.width
let width = 300 / rate
let height = 300
cavWidth.value = 300 / rate
cavWidth1.value = 300
ctx.translate(height / 2, width / 2)
ctx.rotate((270 * Math.PI) / 180)
ctx.drawImage(img, -width / 2, -height / 2, width, height)
canvas.toBlob((fileSrc) => {
blobToDataURI(fileSrc, (dataurl) => {
const imgInfor = idCard + '/' + new Date().getTime()
const blobPath = parseBlob(dataurl)
const File = base64toFile(dataurl, imgInfor)
File.path = blobPath
<!-- qiniuUploadSubscribe(二次封装的上传方法),如果项目上传
图片不需要进行二次封装可直接用uni.uploadFile上传 -->
qiniuUploadSubscribe({
files: [File],
type: 5,
success: (res, index) => {
if (res.code == 20000) {
lista.url = res.data
// setFilePath(全局定义的设置图片访问路径的方法)
lista.imageURL = t.setFilePath(res.data)
lista.percent = 100
lista.uploadStatus = 'SUCCESS'
lista.size = File.size
lista.type = File.type
lista.name = File.name
lista.path = File.path
//将后端接口返回的图片路径返回给父组件
emits('complete', [lista])
}
uni.showToast({
icon: 'none',
title: res.message
})
pointList.value = []
uni.hideLoading();
cancel()
},
error: (err, index) => {
lista.uploadStatus = 'ERROR'
},
progress: (res, index) => {
const {
progress,
totalBytesSent,
totalBytesExpectedToSend
} = res
lista.percent = Math.floor(progress)
lista.uploadStatus = progress < 100 ? 'UPLOADING' :
'SUCCESS'
},
complete: (err, index) => {
console.log(err, index)
},
})
});
})
}
})
}
</script>
<style scoped lang="scss">
.box {
position: absolute;
animation: boxBotToTop .9s;
-webkit-animation: boxBotToTop .9s;
width: 92%;
bottom: 0;
left: 4%;
z-index: 997;
background: #374151;
border-radius: 30rpx;
}
@keyframes boxBotToTop {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
transform: translateY(40px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
transform: translateY(0) scale(.9);
}
}
@-webkit-keyframes boxBotToTop {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
transform: translateY(40px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
transform: translateY(0) scale(.9);
}
}
.canvas-autograph {
position: absolute;
z-index: 998;
height: 95%;
width: 82%;
top: 50%;
left: 50%;
transform: translate(-43%, -50%);
.scroll-view {
width: 100%;
height: 100%;
background-color: #FFFFFF;
}
.fun-box {
position: absolute;
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
transform: rotate(90deg);
top: 50%;
left: -59%;
.fun-box-btn {
width: 160rpx;
height: 100%;
color: #FFFFFF;
border-radius: 20rpx;
border: 1rpx solid #C0C0C0;
text-align: center;
+.fun-box-btn {
margin-left: 20rpx;
}
}
}
}
</style>
组件调用
<template>
<view class="homePage">
<view style="position: absolute;height: 100vh;bottom:0;width:100%" v-if="isCanvas">
<MiliuAutograph v-model="isCanvas" @complete="complete" :infor="{idCard:floatingForm.idCard}" />
</view>
</view>
</template>
<script setup>
import MiliuAutograph from '@/components/MiliuAutograph/index.vue'
import {
ref,
defineComponent,
getCurrentInstance,
reactive
} from 'vue'
const {
proxy: t
} = getCurrentInstance()
const isCanvas = ref(false)
const complete = e => {
console.log(e,'电子签名图片上传成功后,后端返回的图片路径');
}
</script>