Vue如何实现验证码输入交互


Posted in Vue.js onDecember 07, 2020

最近做一个H5的页面,里面有个输入验证码交互,就是移动端比较常见的那种验证码输入交互。就是那种,对,就是那种,一个数字一个下划线,移动端非常常见的那种验证码交互。实现过程中主要参考了美团外卖安卓端的具体交互。

应用到项目中的效果如下。

一般操作:

Vue如何实现验证码输入交互

粘贴效果:

Vue如何实现验证码输入交互

方案选择

方案1:调整文字的间距
设置 input 的 letter-spacing 属性,我们就可以让验证码之间有足够大的空隙,然后再把底线改为有间隔的多个线段貌似就可以了。

然而,这里会有一个问题。就是光标总是会在数字的左边,而我们希望的是输入后的数字的中心位于原来光标的位置。最终我放弃了这个方案。

显然,这个方案并不合适。

方案2:使用多个 input
这就是我使用的方式,也是接下来我要详细讲解的方案。主要原理是:使用多个 input 元素,每个 input 只能输入一个数字。当通过 input 事件监测到字符输入时,自动将焦点对焦到下一个 input 元素。

当然我们还要实现点击任何一个输入框时,将焦点移动到第一个value为空的input上。另外,点击退格键时,也要进行焦点的改变。

测试后后发现,焦点的移动,不会导致移动端键盘的收起。最终我就决定使用这个方案了。

代码实现
在线示例:https://codepen.io/F-star/pen/dyyeZaN

HTML:

<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:

.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:

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 验证码输入交互的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
解决vue使用vant轮播组件swipe + flex时文字抖动问题
Jan 07 Vue.js
详解template标签用法(含vue中的用法总结)
Jan 12 Vue.js
vue+element table表格实现动态列筛选的示例代码
Jan 14 Vue.js
vue实现轮播图帧率播放
Jan 26 Vue.js
Vue单页面应用中实现Markdown渲染
Feb 14 Vue.js
vue+flask实现视频合成功能(拖拽上传)
Mar 04 Vue.js
如何理解Vue前后端数据交互与显示
May 10 Vue.js
Vue通过懒加载提升页面响应速度
May 10 Vue.js
使用Vue3+Vant组件实现App搜索历史记录功能(示例代码)
Jun 09 Vue.js
Vue3中的Refs和Ref详情
Nov 11 Vue.js
vue实现可以快进后退的跑马灯组件
Apr 08 Vue.js
vue3不同环境下实现配置代理
May 25 Vue.js
Vue $attrs &amp; inheritAttr实现button禁用效果案例
Dec 07 #Vue.js
vuex Module将 store 分割成模块的操作
Dec 07 #Vue.js
浅谈Vue使用Elementui修改默认的最快方法
Dec 05 #Vue.js
vue+element_ui上传文件,并传递额外参数操作
Dec 05 #Vue.js
解决vue下载后台传过来的乱码流的问题
Dec 05 #Vue.js
解决Vue-cli3没有vue.config.js文件夹及配置vue项目域名的问题
Dec 04 #Vue.js
vue基于Echarts的拖拽数据可视化功能实现
Dec 04 #Vue.js
You might like
消息持续发送的完整例子
2006/10/09 PHP
php的字符串用法小结
2010/06/08 PHP
$_GET['goods_id']+0 的使用详解
2013/06/06 PHP
destoon实现底部添加你是第几位访问者的方法
2014/07/15 PHP
PHP中“=&gt;
2019/03/01 PHP
checkbox使用示例
2013/08/23 Javascript
Ext4.2的Ext.grid.plugin.RowExpander无法触发事件解决办法
2014/08/15 Javascript
js父页面与子页面不同时显示的方法
2014/10/16 Javascript
IE中document.createElement的iframe无法设置属性name的解决方法
2015/09/14 Javascript
JavaScript中的对象和原型(一)
2016/08/12 Javascript
javascript实现的左右无缝滚动效果
2016/09/19 Javascript
JS实现批量上传文件并显示进度功能
2017/06/27 Javascript
Vue中v-for的数据分组实例
2018/03/07 Javascript
vue项目国际化vue-i18n的安装使用教程
2018/03/14 Javascript
基于webpack4.X从零搭建React脚手架的方法步骤
2018/12/23 Javascript
uni-app自定义导航栏按钮|uniapp仿微信顶部导航条功能
2019/11/12 Javascript
js实现数字滚动特效
2019/12/16 Javascript
一篇文章带你从零快速上手Rollup
2020/09/07 Javascript
[50:05]VGJ.S vs OG 2018国际邀请赛淘汰赛BO3 第二场 8.22
2018/08/23 DOTA
Python  __getattr__与__setattr__使用方法
2008/09/06 Python
Python 错误和异常小结
2013/10/09 Python
详解tensorflow实现迁移学习实例
2018/02/10 Python
Python socket实现简单聊天室
2018/04/01 Python
用python标准库difflib比较两份文件的异同详解
2018/11/16 Python
pygame游戏之旅 python和pygame安装教程
2018/11/20 Python
Python3.5 Json与pickle实现数据序列化与反序列化操作示例
2019/04/29 Python
pyqt5数据库使用详细教程(打包解决方案)
2020/03/25 Python
在pycharm中debug 实时查看数据操作(交互式)
2020/06/09 Python
详解HTML5中download属性的应用
2015/08/06 HTML / CSS
SmartBuyGlasses台湾:名牌眼镜,名牌太阳眼镜及隐形眼镜
2017/01/04 全球购物
美国男女折扣服饰百货连锁店:Stein Mart
2017/05/02 全球购物
软件生产职位结构化面试主要考察要素及面试题库
2015/06/12 面试题
2014年小学德育工作总结
2014/12/05 职场文书
房产公证书格式
2015/01/26 职场文书
职工培训工作总结
2015/08/10 职场文书
一次Mysql update sql不当引起的生产故障记录
2022/04/01 MySQL