uniapp (vue3) cavans implements electronic signature function

  1. Create components

<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">
                    empty
                </van-button>
                <van-button round block type="primary" size="small" @click="confirm">
                    confirm
                </van-button>
                <van-button round block type="danger" size="small" @click="cancel">
                    Cancel
                </van-button>
            </view>
        </view>
    </view>
</template>
<script setup>
    // base64 to Blob format method
    // base64 to file stream method
    import {
        parseBlob,
        base64toFile,
        blobToDataURI
    } from '@/utils/base64ToFile.js'
    // Use Qiniu cloud package image upload method
    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 canvasCtx = reactive(uni. createCanvasContext('mycanvas'))
    canvasCtx.lineWidth = 4;
    canvasCtx.lineCap = 'round'
    canvasCtx.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);
        canvasCtx.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()
        canvasCtx.moveTo(point1.X, point1.Y)
        canvasCtx.lineTo(point2.X, point2.Y)
        canvasCtx.stroke()
        canvasCtx. draw(true)
    }
    const touchend = e => {
        points = [];
    }
    const clear = () => {
        pointList. value = []
        return uni. getSystemInfo()
            .then(res => {
                canvasCtx. clearRect(0, 0, res. windowWidth, res. windowHeight);
                canvasCtx. draw(true);
                return res
            })
            .catch(err => {
                console. log(err);
            })
    }
    const confirm = () => {
        if (pointList. value. length < 10) {
            uni.showToast({
                icon: 'none',
                title: 'Please sign'
            })
        } 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: "Uploading"
        })
        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 (upload method of secondary packaging), if the project is uploaded
                    The picture does not need to be packaged twice and can be uploaded directly with uni.uploadFile -->
                        qiniuUploadSubscribe({
                            files: [File],
                            type: 5,
                            success: (res, index) => {
                                if (res.code == 20000) {
                                    lista.url = res.data
                                    // setFilePath (the globally defined method of setting the image access path)
                                    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
                                    //Return the image path returned by the backend interface to the parent component
                                    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>
  1. component call

<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,'After the electronic signature image is successfully uploaded, the image path returned by the backend');
    }
</script>