需求
那么前端完成一个合格的验证码,究竟需要做成什么样子呢?
首先验证码有个大体的雏形,既然是拖动验证码,那就要拖动块和目标块,我们需要把拖动块拖动到目标块上就算校验成功。
验证码的一个功能就是来规避机器的自动操作,所以我们需要通过轨迹来判断这个拖动过程是真实的人还是机器,因此我们需要==记录拖动的路径,路径经过计算之后可以发送到后端进行进一步的分类,比如对接深度学习模型来分类拖动轨迹是否是人。
以上就是验证码的两个基本要求,所以我们这里就来实现一下看看。
结果
拖拽前:
拖拽成功后:
可以看到图中有一个初始滑块,有一个目标滑块,如果把初始滑块拖动到目标滑块上才能校验成功,然后下方再打印拖动的轨迹,包含它的 x、y 坐标。
有了这些内容之后,就可以放到表单里面进行提交了,轨迹数据可以自行加密处理并校验来判断其是否合法。
具体实现
新建vue项目
vue create drag-captcha
然后找一张不错的风景图,放到 public 目录下,后面我们会引用它。
npm install --save vue-drag-drop--save vue-drag-drop
在mian.js中引入核心的包 vue-drag-drop
import VueDragDrop from 'vue-drag-drop'
Vue.use(VueDragDrop)
首先 vue-drag-drop 提供了两个组件,一个叫做 Drag,一个叫做 Drop。前者是被拖动对象,后者是放置目标,我们利用这两个组件构建两个滑块,将 Drag 滑块拖动到 Drop 滑块上就成功了。因此,我们要做的仅仅是把它们两个声明出来并添加几个检测方法就好了,至于拖动的功能,vue-drag-drop 这个组件已经给我们封装好了。
这里我们就直接在 App.vue 里面修改内容就好了,在 里面先声明一下两个组件:
<template>
<div id="app">
<h2 :style="titleStyle">拖动验证码样例</h2>
<div id="wrapper" :style="wrapperStyle">
<drop class="drop" id="target"
:class="{ 'over': state.over }"
@dragover="onDragOver"
@dragleave="onDragLeave"
:style="targetStyle">
</drop>
<drag class="drag" id="source"
:transfer-data="true"
@dragstart="onDragStart"
@dragend="onDragEnd"
@drag="onDrag" v-if="!state.dragged"
:style="sourceStyle">
<div slot="image" id="float" :style="sourceStyle">
</div>
</drag>
</div>
<div>
<p v-if="state.dragged" id="trace">
拖动轨迹:{{ trace }}
</p>
</div>
</div>
</template>
Drop
对于 Drop 组件来说,它是一个被放置的对象,被拖动滑块会放到这个 Drop 滑块上,这就代表拖动成功了。它有两个主要的事件需要监听,一个叫做 dragover,一个叫做 dragleave,分别用来监听 Drag 对象拖上和拖开的事件。
在这里,分别对两个事件设置了 onDragOver 和 onDragLeave 的回调函数,当 拖动对象放到放置目标上面的时候,就会触发 onDragOver 对象,当拖开的时候就会触发 onDragLeave 事件。
那这样的话我们只需要一个全局变量来记录是否已经将滑块拖动到目标位置即可,比如可以定一个全局变量 state,我们用 over 属性来代表是否拖动到目标位置。
因此 onDragOver 和 onDragLeave 事件可以这么实现:
onDragOver() { //覆盖触发
this.state.over = true
},
onDragLeave() { //拖开触发
this.state.over = false
},
Drag
对于 Drag 组件来说,它是一个被拖动的对象,我们需要将这个 Drag 滑块拖动到 Drop 滑块上,就代表拖动成功了。它有三个主要的s事件需要监听:dragstart、drag、dragend,分别代表拖动开始、拖动中、拖动结束三个事件,我们这里也分别设置了三个回调方法 onDragStart、onDrag、onDragEnd。
对于 onDragStart 方法来说,应该怎么实现呢?这里应该处理刚拖动的一瞬间的动作,由于我们需要记录拖动的轨迹,所以声明一个 trace 全局变量来保存轨迹信息,onDragStart 要做的就是初始化 trace 对象为空,另外记录一下初始的拖动位置,以便后续计算拖动路径,所以可以实现如下:
onDragStart(data, event) {
this.init = {
x: event.offsetX,
y: event.offsetY,
}
this.trace = []
this.state.dragging = true
},
对于 onDrag 方法来说,就是=处理拖动过程中的一系列拖动动作,这里其实就是计算当前拖动的偏移位置,然后把它保存到 trace 变量里面,所以可以实现如下:
onDrag(data, event) {
console.log('event', event)
let offsetX = event.offsetX - this.init.x
let offsetY = event.offsetY - this.init.y
this.trace.push({
x: offsetX,
y: offsetY,
})
},
对于 onDragEnd 方法来说,其实就是检测最后的结果了,刚才我们用 state 变量里面的 over 属性来代表是否拖动到目标位置上,这里我们也定义了另外的 dragged 属性来代表是否已经拖动完成,dragging 属性来代表是否正在拖动,所以整个方法的逻辑上是检测 over 属性,然后对 dragging、dragged 属性做赋值,然后做一些相应的提示,实现如下:
onDragEnd() {
if (this.state.over) {
this.state.dragging = false
this.state.dragged = true
this.$message.success('拖动成功') }
else {
this.state.dragging = false
this.state.dragged = false
this.$message.error('拖动失败')
} this.state.over = false
}if (this.state.over) {
this.state.dragging = false
this.state.dragged = true
this.$message.success('拖动成功')
}
else {
this.state.dragging = false
this.state.dragged = false
this.$message.error('拖动失败')
}
this.state.over = false
}
OK 了,以上便是主要的逻辑实现,这样我们就可以完成拖动滑块的定义以及拖动的监听了。
接下来就是一些样式上的问题了,对于图片的呈现,这里直接使用 CSS 的 background-image 样式来设置的,如果想显示图片的某一个范围,那就用 background-position 来设置,这是几个核心的要点。
好,这里的样式设置其实也可以用 JavaScript 来实现,我们把它们定义为一些计算属性:
wrapperStyle() {
return {
width: this.size.width + 'px',
height: this.size.height + 'px',
backgroundImage: 'url(' + this.image + ')',
backgroundSize: 'cover'
}
},
targetStyle() {
return {
left: this.block.x + 'px',
top: this.block.y + 'px'
}
},
sourceStyle() {
return {
backgroundImage: 'url(' + this.image + ')',
backgroundSize: this.size.width + 'px ' + this.size.height + 'px',
backgroundPosition: -this.block.x + 'px ' + -this.block.y + 'px'
}
}
},
另外这里还有一个值得注意的地方,就是 Drag 组件的 slot 部分:
这部分定义了在拖动过程中随鼠标移动的图片样式,这里也和 Drag 滑块一样定义了一样的样式,这样在拖动的过程中,就会显示一个和 Drag 滑块一样的滑块随鼠标移动。最后,就是拖拽完成之后,将滑动轨迹输出出来,这里我就直接呈现在页面上了, 区域加入如下定义即可:
<div> <p v-if="state.dragged" id="trace"> 拖动轨迹:{{ trace }} </p></div>
<p v-if="state.dragged" id="trace">
拖动轨迹:{{ trace }}
</p>
</div>