发布于 2026-01-06 6 阅读
0

在 Vue 中创建签名板组件

在 Vue 中创建签名板组件

大家好,今天我们将学习如何使用 Vue.js 创建一个签名板组件。

当您有特定需求时,创建自己的组件非常有用,它还可以让您了解组件背后的逻辑。

使用画布

我们将使用canvas HTML 标签,这样用户就可以在上面绘制签名。

<template>
    <canvas />
</template>
Enter fullscreen mode Exit fullscreen mode

增添一些风格:

<style scoped>
canvas {
    border: 1px solid black;
    background-color: white;
    cursor: crosshair;
}
</style>
Enter fullscreen mode Exit fullscreen mode

笔记:

  • 这里我们使用 style 标签上的 scoped 属性,这样就可以将样式保留在组件内部。
  • 我用十字准星定义光标(细节决定成败)。

是时候使用JavaScript了!

首先,我们将获取画布并向其传递一些参数。

data() {
    return {
        ctx :  null,
    }
},
mounted(){
    this.ctx  = this.$el.getContext('2d')
    this.ctx.strokeStyle  =  'black'
    this.ctx.lineWidth  =  2
}
Enter fullscreen mode Exit fullscreen mode

笔记:

  • strokeStyle 是签名的颜色。
  • lineWidth 是签名的宽度。

让我们给画布添加 mousedown 事件,这样我们就能在用户点击画布时知道发生了什么。

<template>
    <canvas @mousedown=”onMouseDown” />
</template>
Enter fullscreen mode Exit fullscreen mode
data(){
    return {
        ...
        sign  : false,
        prevX : null,
        prevY : null
    }
}
methods: {
    onMouseDown($event){
        this.sign = true
        this.prevX = $event.offsetX
        this.prevY = $event.offsetY
    }
}
...
Enter fullscreen mode Exit fullscreen mode
  • sign 属性可以用来判断用户是否点击了画布。
  • prevX 和 prevY 属性允许通过从 $event 中检索来了解光标的当前位置。

我们换到二挡!

我们将为画布添加鼠标移动事件:

<template>
    <canvas ... @mousemove="onMouseMove" />
</template>
Enter fullscreen mode Exit fullscreen mode
methods: {
    ...
    mousemove($event) {
        if(this.sign) {
            const  currX  = $event.offsetX
            const  currY  = $event.offsetY
        }
    },
}
Enter fullscreen mode Exit fullscreen mode

在这里,我们可以获取指针的当前位置,这将使我们能够绘制签名,这要归功于我们在 @onmousedown 事件中获取的先前位置。

画出签名

methods: {
    ...
    mousemove($event) {
        if(this.sign) {
            const  currX  = $event.offsetX
            const  currY  = $event.offsetY
            this.draw(this.prevX, this.prevY, currX, currY)
            this.prevX  =  currX
            this.prevY  =  currY
        }
    },
    draw(depX, depY, destX, destY){
        this.ctx.beginPath()
        this.ctx.moveTo(depX, depY)
        this.ctx.lineTo(destX, destY)
        this.ctx.closePath()
        this.ctx.stroke()
    }
}
Enter fullscreen mode Exit fullscreen mode

评论:

  • beginPath() 允许开始一个路径
  • moveTo() 函数允许初始化起始点
  • lineTo() 函数允许描述到达点
  • closePath() 关闭路径
  • stroke() 函数允许将路径应用到画布上。

现在,如果满足以下条件,我们将阻止用户在画布上绘制:

  • 他的光标在画布外。
  • 他的光标不再点击了
<template>
    <canvas ... @mouseup="sign = false" @mouseout="sign = false" />
</template>
Enter fullscreen mode Exit fullscreen mode

获取 v 模型并存储 canvas。

让我们定义 emit update 和 modelValue props。

emits : ['update:modelValue'],
props : {
    modelValue : {
        type :  null,
        required :  true
    }
},
Enter fullscreen mode Exit fullscreen mode

让我们将画布上的图形转换为图像,并在绘制方法中更新 v 模型:

methods: {
    ...
    draw(depX, depY, destX, destY) {
        this.ctx.beginPath()
        this.ctx.moveTo(depX, depY)
        this.ctx.lineTo(destX, destY)
        this.ctx.closePath()
        this.ctx.stroke()

        const img = this.$el.toDataURL('image/png').replace('image/png',        'image/octet-stream')
        this.$emit('update:modelValue', img)
    }
}
Enter fullscreen mode Exit fullscreen mode

最后一步!

现在我们需要检查组件的 v-model 是否为空,以便移除画布绘制的内容。

watch : {
    modelValue(model) {
        if(!model) {
            this.ctx.clearRect(0, 0, this.$el.width, this.$el.height)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

就是这样!

要在父视图中使用我们的组件,方法如下:

<template>
    <MyCanvasComponent v-model="canvas" />
    <button @click="canvas = null">Delete your signature</button>
</template>
Enter fullscreen mode Exit fullscreen mode
import MyCanvasComponent from '@/components/MyCanvasComponents.vue
export default {
    components : {
        MyCanvasComponent
    },
    data(){
        return {
            canvas : null
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

整个组件代码:

<template>
    <canvas @mousedown="mousedown" @mousemove="mousemove" @mouseup="sign = false" @mouseout="sign = false" />
</template>
Enter fullscreen mode Exit fullscreen mode
export  default {
    emits : ['update:modelValue'],
    props : {
        modelValue : {
            type :  null,
            required :  true
        }
    },
    data() {
        return {
            ctx :  null,
            sign :  false,
            prevX :  0,
            prevY :  0,
        }
    },
    methods : {
        mousedown($event) {
            this.sign  =  true
            this.prevX  = $event.offsetX
            this.prevY  = $event.offsetY
        },
        mousemove($event) {
            if(this.sign) {
                const  currX  = $event.offsetX
                const  currY  = $event.offsetY
                this.draw(this.prevX, this.prevY, currX, currY)
                this.prevX  =  currX
                this.prevY  =  currY
            }
        },
        draw(depX, depY, destX, destY) {
            this.ctx.beginPath()
            this.ctx.moveTo(depX, depY)
            this.ctx.lineTo(destX, destY)
            this.ctx.closePath()
            this.ctx.stroke()

            const img = this.$el.toDataURL('image/png').replace('image/png', 'image/octet-stream')
            this.$emit('update:modelValue', img)
        },
    },
    watch : {
        modelValue(model) {
            if(!model) {
            this.ctx.clearRect(0, 0, this.$el.width, this.$el.height)
            }
        }
    },
    mounted() {
        this.ctx  = this.$el.getContext('2d')
        this.ctx.strokeStyle  =  'black'
        this.ctx.lineWidth  =  2
    }
}
Enter fullscreen mode Exit fullscreen mode
<style scoped>
canvas {
    border: 1px solid black;
    background-color: white;
    cursor: crosshair;
}
</style>
Enter fullscreen mode Exit fullscreen mode
文章来源:https://dev.to/raphbensimon/create-signature-component-in-vue-4djm