最近做一个H5的页面,里面有个输入验证码交互,就是移动端比较常见的那种验证码输入交互。就是那种,对,就是那种,一个数字一个下划线,移动端非常常见的那种验证码交互。实现过程中主要参考了美团外卖安卓端的具体交互。
应用到项目中的效果如下。
一般操作:
粘贴效果:
方案选择
方案1:调整文字的间距
设置 input 的 letter-spacing 属性,我们就可以让验证码之间有足够大的空隙,然后再把底线改为有间隔的多个线段貌似就可以了。
然而,这里会有一个问题。就是光标总是会在数字的左边,而我们希望的是输入后的数字的中心位于原来光标的位置。最终我放弃了这个方案。
显然,这个方案并不合适。
方案2:使用多个 input
这就是我使用的方式,也是接下来我要详细讲解的方案。主要原理是:使用多个 input 元素,每个 input 只能输入一个数字。当通过 input 事件监测到字符输入时,自动将焦点对焦到下一个 input 元素。
当然我们还要实现点击任何一个输入框时,将焦点移动到第一个value为空的input上。另外,点击退格键时,也要进行焦点的改变。
测试后后发现,焦点的移动,不会导致移动端键盘的收起。最终我就决定使用这个方案了。
代码实现
在线示例:https://codepen.io/F-star/pen/dyyeZaN
HTML:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
< div id = "app" > < div class = "captcha" > < input v-for = "(c, index) in ct" :key = "index" type = "number" v-model = "ct[index]" ref = "input" :style = "{borderBottomColor: index <= cIndex ? '#333' : ''}" @input="e => {onInput(e.target.value, index)}" @keydown.delete="e=>{onKeydown(e.target.value, index)}" @focus="onFocus" :disabled="loading" > </ div > < p >{{msg}}</ p > </ div > |
CSS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
.captcha { display : flex; justify- content : center ; margin-top : 40px ; } input { margin-right : 20px ; width : 45px ; text-align : center ; border : none ; border-bottom : 1px solid #eee ; font-size : 24px ; outline : none ; } input:last-of-type { margin-right : 0 ; } input:disabled { color : #000 ; background-color : #fff ; } .msg { text-align : center ; } |
JS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
var Main = { data() { return { ct: [ '' , '' , '' , '' , '' , '' ], loading: false , msg: '' , } }, computed: { ctSize() { return this .ct.length; }, cIndex() { let i = this .ct.findIndex(item => item === '' ); i = (i + this .ctSize) % this .ctSize; return i; }, lastCode() { return this .ct[ this .ctSize - 1]; } }, watch: { cIndex() { this .resetCaret(); }, lastCode(val) { if (val) { console.log( 'this.ctSize' , this .ctSize) this .$refs.input[ this .ctSize - 1].blur(); this .sendCaptcha(); } } }, mounted() { this .resetCaret(); }, methods: { onInput(val, index) { this .msg = '' val = val.replace(/\s/g, '' ); if (index == this .ctSize - 1) { this .ct[ this .ctSize - 1] = val[0]; // 最后一个码,只允许输入一个字符。 } else if (val.length > 1) { let i = index; for (i = index; i < this .ctSize && i - index < val.length; i++) { this .ct[i] = val[i]; } this .resetCaret(); } }, // 重置光标位置。 resetCaret() { this .$refs.input[ this .ctSize-1].focus(); }, onFocus() { // 监听 focus 事件,将光标重定位到“第一个空白符的位置”。 let index = this .ct.findIndex(item => item === '' ); index = (index + this .ctSize) % this .ctSize; console.log( this .$refs.input) this .$refs.input[index].focus(); }, onKeydown(val, index) { if (val === '' ) { // 删除上一个input里的值,并对其focus。 if (index > 0) { this .ct[index - 1] = '' ; this .$refs.input[index - 1].focus(); } } }, sendCaptcha() { console.log(); this .msg = `发送验证码到服务器:${ this .ct.join( '' )}`; // 此时无法操作 input。。 this .loading = true ; setTimeout(() => { this .msg = ( '验证码错误' ) this .loading = false ; this .$nextTick(() => { this .reset(); }) }, 3000) }, reset() { // 重置。一般是验证码错误时触发。 this .ct = this .ct.map(item => '' ); this .resetCaret(); } } } var Ctor = Vue.extend(Main) new Ctor().$mount( '#app' ) |
原理
创建多个 input 元素,对这些 input 都绑定 focus 事件。一旦触发该事件,我们会把焦点移动到从左往右第一个 value 为空字符的 input 上。所以在初始状态时,点击最右边的 input,光标还是会跑到最左边的 input。
然后我们给这些 input 绑定 input 事件,监听输入字符。当输入后的字符不为空字符,我们会和 focus 事件一样,重定位下一个需要聚焦的 input。如果输入的是多个字符(一般是是粘贴的缘故),就会把多出来的字符一个一个按顺序填入到后面的 input 中,然后才重定位光标。这样,我们就实现了一个个输入数字和粘贴短信验证码(一次性输入多个数字)的交互。
最后我们还要处理退格行为,需要给所有 input 绑定 keydown 事件。当按下的为退格键,且当前 input 的 value 为空时,清空上一个 input 里的数据,并聚焦到上一个 input 上。
对了,验证码输入错误后,需要清除所有 input 的数据,并把焦点移动到第一个 input 上。
总结
原理并不复,只是实现起来有点繁琐。
我这个方案没有进行浏览器兼容,请大家在经过充分的测试后再行使用。
如果可以的话,我还是推荐简单的一个输入框方案,而不是选择这种花里胡哨的交互。简单稳妥的实现维护简单,也不会有太多意想不到的状况。因为验证码输入这里如果在某些浏览器上无法正确操作,对转化率还是有很大影响的。
以上就是Vue如何实现验证码输入交互的详细内容,更多关于vue 验证码输入交互的资料请关注服务器之家其它相关文章!
原文链接:https://blog.fstars.wang/