Vue implements canvas electronic signature

1. The current code is typed out line by line by myself.

<template>
    <div style="margin-left: 28%;">
        <!-- 画布对象 -->
        <div ref="grapDiv" style="width:800px;height:400px;border: 2px dotted #ddd; border-radius: 10px;" v-show="!isCommit">
            <canvas ref="grapCvs" id="container" @mousedown.stop="mousedown" @mousemove.stop="mousemove"/>
        </div>

        <!-- 设置面板 -->
        <div ref="setControlDiv"
            style="width:280px;height:200px;background-color:#474747;border:1px solid #ddd;border-radius: 10px;margin-top: -203px;margin-left: 1px;position:absolute;"
            v-show="ifSetController">
            <div style="width:100%;height:30%;margin-top: 10px;">
                <span style="width:100%;height:30%;">
                    <label style="float:left;color:white;margin-left: 14px;margin-top: 15px;">字体大小</label>
                    <label style="float:right;color:white;margin-right: 14px;margin-top: 15px;">{
    
    {
    
    fontSize}}</label>
                </span>
                <el-div style="width:100%;height:20%;margin-top: 40px;">
                    <el-slider v-model="fontSize" style="width:89%;margin-top:20px;margin-left:17px;" :min="1" :max="10" />
                </el-div>
            </div>
            <div style="width:100%;height:45%;margin-top: 15px;padding: 7px;">
                <li @click="setImgColor" class="" v-for="(item, index) in fontColorArray" :key="index" :style="'list-style: none;width: 38px;height: 38px;float: left;background: ' + item"></li>
            </div>
        </div>
        <div ref="setDiv" v-show="!isCommit && ifSet">
            <img title="画笔设置" src="../assets/brush.png" @click="setGraph" style="margin-top:4px;float: left;margin-left: 15px;cursor: pointer;" />
        </div>

        <!-- 提交的画布对象 -->
        <div style="width:800px;height:400px;border:2px solid green; border-radius: 10px;" v-show="isCommit">
            <img :src="content">
        </div>
        
        <!-- 操作按钮 -->
        <div style="width:800px;padding-top: 10px;">
            <el-button @click="set()" v-show="!isCommit">画笔设置</el-button>
            <el-button @click="clear()" v-show="!isCommit">清除</el-button>
            <el-button type="primary" @click="commit()" v-show="!isCommit">提交</el-button>
            <el-button @click="goback()" v-show="isCommit">返回</el-button>
        </div>
    </div>
</template>

<style>
  /* 选中的li改变样式 */
  .activeteLi {
    
    
    box-shadow: 0 0 3px rgb(0 0 0 / 95%);
    transform: scale(1.2);
  }
</style>

<script>
import {
    
     ElMessage, ElMessageBox } from 'element-plus'
export default {
    
    
    data() {
    
    
        return {
    
    
            canvas : null,       // cvs对象
            graphics: null,      // 画笔对象
            isDrawing : false,   // 是否可以进行画
            curMouseY : null,    // 当前鼠标在屏幕中的Y轴坐标
            curMouseX: null,     // 当前鼠标在屏幕中的X轴坐标
            offsetY: null,       // cvs在屏幕中的Y轴偏移量
            xoffsetX: null,      // cvs在屏幕中的X轴偏移量
            cvsWidth: 800,       // cvs的宽度
            cvsHeight: 400,      // cvs的高度
            isCommit: false,     // 画笔是否提交
            content: null,       // 画布提交的内容
            ifGraph: false,      // 是否画过(提交的用于检验是否有签字过)
            ifSet: false,        // 是否打开设置面板
            ifSetController: false,  // 是否打开画笔设置面板
            fontSize: 1,          // 字体大小
            fontColorArray: ["#F59999", "#E86262", "#AA4446", "#6B4849",
                "#34231E", "#435772", "#2DA4A8", "#EFDCD3", "#FEAA3A",
                "#FD6041", "#CF2257", "#404040", "#92BEE2", "#2286D8"],  //画笔可选的颜色
            fontColor: "#000000"  // 画笔默认颜色
        }
    },
    methods: {
    
    
        // 颜色点击事件
        setImgColor(curIndex) {
    
    
            // 先将所有的li标签设置class为空
            let liArray = curIndex.currentTarget.parentElement.children;
            for (let child of liArray) {
    
    
                child.className = ""
            }

            // 在将当前li,添加指定的class
            curIndex.currentTarget.className = "activeteLi"
            this.fontColor = curIndex.currentTarget.style.background;
        },
        // 设置样式栏
        setGraph() {
    
     
            // 打开关闭设置面板
            this.ifSetController = this.ifSetController ? false : true
        },
        // 鼠标点击按下事件
        mousedown(e) {
    
     
            // 鼠标点击的时候设置,变量标志可以画了
            this.isDrawing = true;
            // 获取当前鼠标的位置(鼠标在屏幕中的坐标减去容器在屏幕中的偏移量即可得到鼠标当前的坐标)
            this.curMouseX = e.pageX - this.offsetX;
            this.curMouseY = e.pageY - this.offsetY;
            // 从当前鼠标的位置开始画
            this.graphics.beginPath()
            this.graphics.moveTo(this.curMouseX, this.curMouseY);

            // 鼠标离开,则关闭控制面板
            this.ifSetController = false
            this.ifSet = false
            this.$refs.grapDiv.style.cssText = "width:800px;height:400px;border: 2px dotted #ddd; border-radius: 10px;"
        },
        // 鼠标移动事件
        mousemove(e) {
    
    
            // 可以画的标志为true,代表可以进行画了
            if (this.isDrawing) {
    
    
                // 设置画笔属性
                this.graphics.strokeStyle = this.fontColor;
                this.graphics.lineWidth = this.fontSize;

                // 设置从哪里开始画
                this.curMouseX = e.pageX - this.offsetX
                this.curMouseY = e.pageY - this.offsetY
                // 获取当前鼠标的位置(鼠标在屏幕中的坐标减去容器在屏幕中的偏移量即可得到鼠标当前的坐标)
                this.graphics.lineTo(this.curMouseX, this.curMouseY)
                this.graphics.stroke()
                // 设置当前已经开始绘画了
                this.ifGraph = true
            }
        },
        // 打开设置面板
        set() {
    
    
            // 关闭设置面板
            if (this.ifSet) {
    
    
                this.ifSetController= false
                this.ifSet = false
                this.$refs.grapDiv.style.cssText = "width:800px;height:400px;border: 2px dotted #ddd; border-radius: 10px;"
            // 打开设置面板    
            } else {
    
     
                this.ifSet = true
                this.$refs.grapDiv.style.cssText = "width:800px;height:400px;border: 2px dotted #ddd; border-radius: 10px 10px 0px 0px;"
                this.$refs.setDiv.style.cssText = "width:800px;height:45px;border: 2px dotted #ddd;border-top:none;border-radius:0px 0px 10px 10px;"
            }
        },
        // 清除画布
        clear() {
    
    
            this.ifGraph = false
            this.$refs.grapCvs.width = this.$refs.grapCvs.width
        },
        // 提交画布
        commit() {
    
    
            // 内容过滤
            if (!this.ifGraph) {
    
    
                ElMessage({
    
     message: "没有可提交的内容!", type: 'error', duration:2000 })
                return
            }

            // 提交画布内容
            ElMessageBox({
    
    
                title: '操作提示',          //MessageBox 标题
                message: '确定提交?',       //MessageBox 消息正文内容
                confirmButtonText: '确定',  //确定按钮的文本内容
                cancelButtonText: '取消',   //取消按钮的文本内容
                showCancelButton: true,    //是否显示取消按钮
                closeOnClickModal: false,  //是否可通过点击遮罩关闭
                type: 'warning',           //消息类型,用于显示图标
            }).then(() => {
    
    
                this.isCommit = true
                this.ifSetController = false
                this.ifSet = false
                this.content = this.canvas.toDataURL()
                ElMessage({
    
     message: "提交成功!", type: 'success', duration: 1000 })
            });
        },
        // 返回
        goback() {
    
    
            this.ifGraph = false
            this.$refs.grapCvs.width = this.$refs.grapCvs.width
            this.isCommit = false
        }
    },
    mounted() {
    
    
        // 获取cvs的容器
        var canvas = document.getElementById("container");
        canvas.width = this.cvsWidth;            // 设置cvs的宽度
        canvas.height = this.cvsHeight;          // 设置cvs的高度
        this.offsetY = canvas.offsetTop;         // 设置容器在屏幕中的顶部偏移量
        this.offsetX = canvas.offsetLeft;        // 设置容器在屏幕中的左边偏移量
        this.graphics = canvas.getContext("2d"); // 获取2d类型的画布对象
        this.canvas = canvas;                    // cvs对像

        // 鼠标松开后,就不允许在画
        document.addEventListener('mouseup', e => {
    
    
            this.isDrawing = false;
            this.graphics.closePath()
        })
    }
};
</script>

2. Effect
insert image description here
3, base64 encoding after submission.
insert image description here
The above is a one written by me, which is only suitable for PC.
Finally, I found a better plug-in, which can be directly quoted and recorded.

Click me to view the demo

Function description:
1. Compatible with PC and Mobile;
2. Canvas adapts to screen size changes (the canvas does not need to be reset when the window is zoomed and the screen is rotated, and the coordinate offset is automatically corrected);
3. Custom canvas size (export image size), brush Thickness, color, canvas background color;
4. Support cropping (for demand: some signatures need to crop the surrounding blank space).
5. The exported image format is base64;

Step 1: Install: npm install vue-esign --save
Step 2: Use: Introduce the plugin in main.js
import vueEsign from 'vue-esign'
Vue.use(vueEsign)

Step 3: Page usage instructions:
ref must be set to call the two built-in methods reset() and generate() of the component. There is no need to set the width and height of the style for the component, if the value of the width attribute of the canvas does not exceed the style width of the parent element , the style width of the component is the canvas width, if it exceeds, the component style width is 100% of the parent element; so just set the width of the parent element;

<vue-esign ref="esign" :width="800" :height="300" :isCrop="isCrop" :lineWidth="lineWidth" :lineColor="lineColor" :bgColor.sync="bgColor" />
<button @click="handleReset">清空画板</button> 
<button @click="handleGenerate">生成图片</button>
export default {
    
    
	data () {
    
    
		 return {
    
    
			 lineWidth: 6,
			 lineColor: '#000000',
			 bgColor: '',
			 resultImg: '',
			 isCrop: false
		 }
	},
	methods: {
    
    
		 handleReset () {
    
    
			 this.$refs.esign.reset()
		 },
		 handleGenerate () {
    
    
			 this.$refs.esign.generate().then(res => {
    
    
			 this.resultImg = res
		 }).catch(err => {
    
    
		 	alert(err) // 画布没有签字时会执行这里 'Not Signned'
		 })
	}
}

Plugin description:

Attributes type Defaults illustrate
width Number 800 Canvas width, that is, the width of the exported image
height Number 300 Canvas height, that is, the height of the exported image
lineWidth Number 4 brush thickness
lineColor String #000000 brush color
bgColor String null Canvas background color, when it is empty, the canvas background is transparent, supports multiple formats'#ccc','#E5A1A1','rgb(229, 161, 161)','rgba(0,0,0,.6)', 'red'
isCrop Boolean false Whether to crop, to cut off the surrounding blank parts based on the size set by the canvas

Plus two built-in methods:
clear the canvas
this.$refs.esign.reset()

Generate image
this.$refs.esign.generate().then(res => { console.log(res) // base64 image }).catch(err => { alert(err) // will be executed when the canvas is not signed here 'Not Signed' })



Guess you like

Origin blog.csdn.net/gelinwangzi_juge/article/details/127566666