VUEは電子署名を実装します

 何も言わずにコードに直接アクセスしてください

<template>
    <div>
        <div class="block-panel">
            <canvas ref="canvas" class="canvas" @mousedown="beginningDraw" 
            @mouseup="endingDraw"@mouseleave="drawLeave" @mousemove="drawing"           
            v-show="!isSaved"></canvas>
            <div class="picture" v-show="isSaved">
                <img :src="picture" alt="签名异常">
            </div>
            <div class="btns-box">
                <div style="text-align: center">
                    <el-button type="plain" @click="canvasUndo">撤销</el-button>
                    <el-button type="plain" @click="canvasRedo">反撤销</el-button>
                </div>
                <div style="text-align: center">
                    <el-button type="primary" @click="canvasClear">清空</el-button>
                    <el-button type="primary" @click="signAgain">重新签</el-button>
                    <el-button type="primary" @click="saveAsImg">保存</el-button>
                </div>
            </div>
        </div>
    </div>
</template>
import axios from "axios"
export default {
    data(){
        return{
            // 图片路径
            picture: '',
            // 画布
            emptyCanvas: '',
            // 画布操作记录
            step: -1,
            canvas: '',
            // 画布操作历史
            canvasHistory: [],
            // 画布参数
            obj: {},
            // 是否已保存生成图片
            isSaved: false
        }
    },
    mounted(){
        this.init()
        this.canvasInit()
    },
    methods: {
        /**
         * 初始化画布数据
         */
        init(){
            this.canvas = this.$refs.canvas
            this.obj = {
                canvas: this.canvas,
				context: this.canvas.getContext("2d"),
				isWrite: false,	//是否开始
				lastWriteTime: -1,	
				lastWriteSpeed: 0,	
				lastWriteWidth: 0,
				canvasWidth: 800, // canvas宽
				canvasHeight: 400, // canvase高
				bgColor: '#fff', // 背景色
				borderWidth: 2, // 网格线宽度
				borderColor: "#fff", //网格颜色	
				lastPoint: {}, //
				writeWidth: 2, //基础轨迹宽度
				maxWriteWidth: 30, // 写字模式最大线宽
				minWriteWidth: 1, // 写字模式最小线宽
				writeColor: '#000',	// 轨迹颜色
				isWriteName: true // 签名模式
			}
        },
        /**
         * 设置画布线条宽度
         */
        setLineWidth(){
            let nowTime = new Date().getTime();
            let diffTime = nowTime - this.obj.lastWriteTime;
            this.obj.lastWriteTime = nowTime;
            let returnNum = this.obj.minWriteWidth + (this.obj.maxWriteWidth - this.obj.minWriteWidth) * diffTime / 30;
            if(returnNum < this.obj.minWriteWidth) {
                returnNum = this.obj.minWriteWidth;
            } else if(returnNum > this.obj.maxWriteWidth) {
                returnNum = this.obj.maxWriteWidth;
            }
            
            returnNum = returnNum.toFixed(2);
            //写字模式和签名模式
            if(this.obj.isWriteName){
                this.obj.context.lineWidth = this.obj.writeWidth;
            }else{
                this.obj.context.lineWidth = this.obj.lastWriteWidth = this.obj.lastWriteWidth / 4 * 3 + returnNum / 4;
            }
        },
        /**
         * 画布中书写
         */
        writing(point){
            this.obj.context.beginPath();
            this.obj.context.moveTo(this.obj.lastPoint.x, this.obj.lastPoint.y);
            this.obj.context.lineTo(point.x, point.y);
            this.setLineWidth();
            this.obj.context.stroke();
            this.obj.lastPoint = point;
            this.obj.context.closePath();
        },
        /**
         * 书写样式
         */
        writeContextStyle() {
            this.obj.context.beginPath();
            this.obj.context.strokeStyle = this.obj.writeColor;
            this.obj.context.lineCap = 'round';
            this.obj.context.lineJoin = "round";
        },
        /**
         * 开始书写
         */
        writeBegin(point) {
            this.obj.isWrite = true;
            this.obj.lastWriteTime = new Date().getTime();
            this.obj.lastPoint = point;
            this.writeContextStyle();
        },
        /**
         * 结束书写
         */
        writeEnd(type = 0) {
            //新增记录
            if (type == 1) {
                this.step++;
                if (this.step < this.canvasHistory.length) {
                    this.canvasHistory.length = this.step; // 截断数组
                }
                this.canvasHistory.push(this.canvas.toDataURL("image/png")); // 添加新的绘制到历史记录
                console.log(this.step);
            }
            //end
            this.obj.isWrite = false;
        },
        /**
         * 清空画板
         */
        canvasClear() {
            this.obj.context.strokeStyle = '#fff';
            this.obj.context.clearRect(0, 0, this.obj.canvasWidth, this.obj.canvasHeight);
            this.step = -1
            this.canvasHistory = []
            if(!this.obj.isWriteName) {
                this.obj.context.beginPath();
                var size = this.obj.borderWidth / 2;
                //画外面的框
                this.obj.context.moveTo(size, size);
                this.obj.context.lineTo(this.obj.canvasWidth - size, size);
                this.obj.context.lineTo(this.obj.canvasWidth - size, this.obj.canvasHeight - size);
                this.obj.context.lineTo(size, this.obj.canvasHeight - size);
                this.obj.context.closePath();
                this.obj.context.lineWidth = this.obj.borderWidth;
                this.obj.context.strokeStyle = this.obj.borderColor;
                this.obj.context.stroke();
                //画里面的框
                this.obj.context.moveTo(0, 0);
                this.obj.context.lineTo(this.obj.canvasWidth, this.obj.canvasHeight);
                this.obj.context.lineTo(this.obj.canvasWidth, this.obj.canvasHeight / 2);
                this.obj.context.lineTo(this.obj.canvasWidth, this.obj.canvasHeight / 2);
                this.obj.context.lineTo(0, this.obj.canvasHeight / 2);
                this.obj.context.lineTo(0, this.obj.canvasHeight);
                this.obj.context.lineTo(this.obj.canvasWidth, 0);
                this.obj.context.lineTo(this.obj.canvasWidth / 2, 0);
                this.obj.context.lineTo(this.obj.canvasWidth / 2, this.obj.canvasHeight);
                this.obj.context.stroke();
            }
        },
        /**
         * 保存图片 格式base64
         */
        saveAsImg() {
            // 将cavase生成base64文件
            let imageSrc = this.canvas.toDataURL("image/png");
            if(imageSrc == this.emptyCanvas) {
                alert('请先书写')
            } else {
                this.isSaved = true
                // console.log('提交的内容===>', imageSrc)
                // 将base64转成file格式
                let blob = this.dataURLtoBlob(imageSrc);
                let file = this.blobToFile(blob, "imgName");
                // 将blob图片转化路径图片
                this.picture = window.URL.createObjectURL(file)
                console.log('this.picture', this.picture)
                let formData = new FormData()
                formData.append('file', this.picture)
                axios({
                    method: 'post',
                    url: '/user/12345',
                    data: formData
                })
            }
        },
        /**
         * 初始化画布并备份
         */
        canvasInit() {
            this.canvas.width = this.obj.canvasWidth;
            this.canvas.height = this.obj.canvasHeight;
            this.emptyCanvas = this.canvas.toDataURL("image/png");
        },
        /**
         * 撤销
         */
        canvasUndo() {
            console.log(this.step)
            if (this.step >= 0) {
                this.step--;
                this.obj.context.clearRect(0, 0, this.obj.canvasWidth, this.obj.canvasHeight);
                if (this.step < 0) {
                    return false
                }
                let canvasPic = new Image();
                canvasPic.src = this.canvasHistory[this.step];
                canvasPic.addEventListener('load', () => {
                    this.obj.context.drawImage(canvasPic, 0, 0);
                });
            } else {
                console.log('不能再继续撤销了');
            }
        },
        /**
         * 反撤销
         */
        canvasRedo() {
            if (this.step < this.canvasHistory.length - 1) {
                this.step++;
                let canvasPic = new Image();
                canvasPic.src = this.canvasHistory[this.step];
                canvasPic.addEventListener('load', () => {
                    this.obj.context.clearRect(0, 0, this.obj.canvasWidth, this.obj.canvasHeight);
                    this.obj.context.drawImage(canvasPic, 0, 0);
                });
            } else {
                console.log('已经是最新的记录了');
            }
        },
        setWidth(){
            //宽度铺满
            width = document.body.clientWidth;
            console.log(width);
            this.obj.canvasWidth =width;

        },
        beginningDraw(e){
            let point = {
                x: e.offsetX || e.clientX,
                y: e.offsetY || e.clientY
            };
            this.writeBegin(point);
        },
        endingDraw(e){
            let point = {
                x: e.offsetX,
                y: e.offsetY
            };
            this.writeEnd(1);
        },
        drawLeave(e){
            let point = {
                x: e.offsetX,
                y: e.offsetY
            };
            this.writeEnd();
        },
        drawing(e){
            if(this.obj.isWrite) {
                let point = {
                    x: e.offsetX,
                    y: e.offsetY
                };
                this.writing(point);
            }
        },
        signAgain(){
            this.isSaved = false
            this.picture = ''
            this.canvasClear()
        },
        dataURLtoBlob(dataurl){
            let 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 Blob([u8arr], {
                type: mime
            });
        },
        blobToFile(theBlob, fileName) {
            theBlob.lastModifiedDate = new Date().toLocaleDateString();
            theBlob.name = fileName;
            return theBlob;
        }
    }
}
.block-panel {
    display: flex;
    align-items: center;
    justify-content: space-between;
    &>.canvas {
        border: 1px solid #303133;
    }
    &>.picture{
        width: 800px;
        height: 400px;
    }
    &>.btns-box {
        padding-right: 20px;
        &>div{
            margin-bottom: 10px;
        }
    }
}

おすすめ

転載: blog.csdn.net/m0_46114541/article/details/129155685