原生JavaScript实现滑动拖动验证的示例代码


Posted in Javascript onDecember 06, 2019

本文介绍了原生JavaScript实现滑动拖动验证的示例代码,分享给大家,具体如下:

原生JavaScript实现滑动拖动验证的示例代码

通常,我们为了防止用户恶意提交表单,会让用户在提交前完成滑动拖动验证,有时候这也能起到一丝反爬的作用。

实现滑动验证的方式当然不止一种,这里我们直接使用原生 JavaScript 来实现。

现在,你可以在这里 看到完整的源码。

原生实现

原生 JavaScript 的实现,主要是通过监听鼠标事件来对 DOM 进行一系列的操作。

滑块验证的结构主要分为四个部分:轨道、滑块、背景和文案,我们可以使用下面的 HTML 结构来表示。

<div class="slide-track">
 <div class="slide-bg"></div>
 <div class="slide-block"></div>
 <p class="slide-text">请按住滑块,拖动到最右边</p>
</div>

基本思路就是我们给滑块(.slide-block)添加相应的事件,在按下滑块时记录鼠标的当前位置并添加滑动事件,在滑动过程中根据鼠标的移动来移动滑块的位置和增加背景元素(.slide-bg)的宽度,直到移动到轨道(.slide-track)的末端后,改变文案(.slide-text)来提示成功。

样式

在开始写脚本之前可以先来完成一下它们的样式,这让滑块相关的部分看起来更好,也让后面的工作更愉快的进行。

/* 样式的注意事项 */

样式的写法就不贴了,相信大家一看就懂,而且会有更好的实现。需要的话,也可以在 Github 上找到。

脚本

现在开始来实现脚本的内容,首先我们对 document.querySelector 方法进行简单的封装以方便后续操作 DOM 元素。

function $(selectors) {
 return document.querySelector(selectors);
}

然后通过自定义的 _h 函数我们可以很方便的创建上面的 HTML 结构,并添加到文档中。

function _h(tagName, propMap = {}, text) {
 const ele = document.createElement(tagName);
 Object.keys(propMap).forEach(prop => ele.setAttribute(prop, propMap[prop]));
 if (text) {
  ele.appendChild(document.createTextNode(text));
 }
 return ele;
}

class SlideUnlock {
 constructor(el = "body", options = {}) {
  this.$el = $(el)
  this.$$isSuccess = false
  this.$options = { // 默认配置
   tip: '请按住滑块,拖动到最右边',
   unlockText: '验证成功',
   duration: 500,
   ...options
  }
 }

 init() {
  this.$$root = _h("div", { class: "slide-track" }) // 轨道
  this.$$bg = this.$$root.appendChild(_h("div", { class: "slide-bg" }))
  this.$$block = this.$$root.appendChild(
   _h("div", { class: "slide-block" }) // 滑块
  )
  this.$$text = this.$$root.appendChild(
   _h("p", { class: "slide-text" }, this.$options.tip)
  )
  this.$el.insertBefore(this.$$root, this.$el.firstChild)
 }
}

在创建好 DOM 结构后,接下来为滑块添加鼠标按下的事件,在这个事件中我们需要记录下鼠标的初始横坐标,以便后续和滑动过程中的位置相比较,同时为其添加滑动事件。

class SlideUnlock {
 init() {
  /* ... */
  this.$$block.addEventListener(
   "mousedown",
   (this.$$handleMouseDown = this._handleMouseDown.bind(this)),
   false
  )
 }

 _handleMouseDown(e) {
  const downx = e.clientX

  e.target.addEventListener(
   "mousemove",
   (this.$$handleMouseMove = this._handleMouseMove.bind(this, downx)),
   false
  )
  e.preventDefault()
 }

 _handleMouseMove(downx, e) {}
}

在这里有点细节需要注意:

  • 首先,由于事件监听器中的 this 指向的是触发事件的元素,为此我们在指定鼠标按下的监听器时为其绑定了 this,以便调用滑块实例属性和原型上的方法。
  • 其次,我们在鼠标按下的监听器中添加了鼠标移动的监听器,如果在初始时同按下的监听器一起指定,那么会先执行鼠标移动的监听器,而此时并没有记录鼠标的初始位置;

接下来我们要实现滑动过程中的主要逻辑:根据鼠标的移动实时地更新滑块的位置,并对一些临界位置进行处理。

_handleMouseMove(downx, e) {
 const info = this.$$block.getBoundingClientRect(),
  x = e.clientX,
  y = e.clientY,
  x1 = info.left,
  y1 = info.top,
  x2 = info.right,
  y2 = info.bottom,
  moveX = x - downx

 if (this.$$isSuccess) {
  return
 }
 if (moveX < 0) {
  return
 }
 if (x < x1 || x > x2 || y < y1 || y > y2) {
  // 当鼠标移开滑块时取消移动
  return
 }

 this.$$block.style.left = `${moveX}px` // 更新滑块的我i之
 this.$$bg.style.width = `${moveX}px` // 同步增大背景元素的宽度

 // 当滑块滑动的距离大于等于轨道除去滑块宽度后的距离时表示已经到达轨道的最右边了
 if (moveX >= this.$$root.offsetWidth - (x2 - x1)) {
  this.$$isSuccess = true
  this.$$text.textContent = "验证成功"
  this.$$text.style.cssText = `color: #fff; left: 0; right: ${this.$$block.offsetWidth}px`
  this.$$block.classList.add("success")
 }
}

这里的实现也很简单,唯一需要看一下的就是通过 getBoundingClientRect 来获取了滑块相对于视口的位置,然后根据鼠标所在的位置来判断鼠标是否在滑块上,如果不在则取消移动。

现在它已经能很好的滑动,并完成提示成功的基本功能了。但是,当我们每次滑动到中间就取消,然后再次点击滑动时,就会导致重复的添加滑动事件,而且中途释放后,滑块就停在了当前位置,这显然不对。

解决的办法就是在添加鼠标按下事件的时候,同时也指定一个松开的事件,在这个事件的监听器中判断如果没有成功则取消之前绑定的滑动事件,并进行重置,为了看起来更友好,我们还可以加上一点动画。

class SlideUnlock {
 init() {
  /* ... */
  document.addEventListener(
   "mouseup",
   (this.$$handleMouseUp = this._handleMouseUp.bind(this)),
   false
  )
 }

 _handleMouseDown(e) {
  /* ... */
  // 取消在手动滑动过程中的动画效果
  this.$$bg.style.transition = ""
  this.$$block.style.transition = ""
  /* ... */
 }

 _handleMouseUp(e) {
  this.$$block.removeEventListener(
   "mousemove",
   this.$$handleMouseMove,
   false
  )

  if (this.$$isSuccess) {
   return
  }

  // 给重置过程添加动画效果
  this.$$bg.style.transition = "width 1s ease"
  this.$$block.style.transition = "left 1s ease"

  this.$$block.style.left = 0
  this.$$bg.style.width = 0
 }
}

目前为止,滑块已经可以在 PC 端正常的工作了,不过在移动端却并不理想。

为了它能够在移动端也可以很好的工作,我们可以借助 touchstarttouchmovetouchend 等事件来完成。

绑定这些事件的时机和处理方式和之前的三个事件分别相对应,所以我们新增两个方法(和 jQuery 的 on、off 方法很像)来更好的添加和移除事件。

function bindEvents(events, handler, element = $("body")) {
 events.split(" ").forEach(event => {
  element.addEventListener(event, handler, false)
 })
}

function unbindEvents(events, handler, element = $("body")) {
 events.split(" ").forEach(event => {
  element.removeEventListener(event, handler, false)
 })
}

根据这两个方法,我们来稍微修改一下滑块中添加事件的代码。

function bindEvents(events, handler, element = $("body")) {
 events.split(" ").forEach(event => {
  element.addEventListener(event, handler, false)
 })
}

function unbindEvents(events, handler, element = $("body")) {
 events.split(" ").forEach(event => {
  element.removeEventListener(event, handler, false)
 })
}

另外,需要注意的是在移动端 touch 事件中获取 clientXclientY 时不能在事件对象上直接读取,而是在 event.changedTouches[0] 对象上取得。

现在,它已经能够同时在 PC 端和移动端上工作了,不过我们还能对它进行一些优化,比如使用函数节流。

函数节流的实现方式有很多,这里列一下我们在本次过程中使用的方式。

utils.throttle = function(method, context = {}, delay = 4, ...outParams) {
 return function(...innerParams) {
 clearTimeout(context.$$tId)
 context.$$tId = setTimeout(function() {
  method.apply(context, [...outParams, ...innerParams])
 }, delay)
 }
}

然后用这个节流函数,来包装我们移动时的处理函数,并根据实际情况做点调整。

除此之外,我们还可以添加一个重置的方法,让它回到最初的状态,涉及到的内容也很简单,就是在成功的状态下重新设置样式和绑定事件。

reset() {
 if (!this.$$isSuccess) {
  return
 }
 this.$$isSuccess = false
 this.$$bg.style.cssText =
  `transition: width ${this.$options.duration}ms ease; width: 0;`
 this.$$block.style.cssText =
  `transition: left ${this.$options.duration}ms ease; left: 0;`
 this.$$text.style.cssText =
  `color: #5f5f5f; left: ${this.$$block.offsetWidth}px; right: 0;`
 this.$$text.textContent = this.$options.tip
 this.$$block.classList.remove("success")
 this._bindEvents()
}

好了,滑块的实现到这里就告一段落了,相信大家看到这里已经完全明白了,甚至有更好的实现。

如何使用

你可以简单的在 HTML 页面中引入该脚本,然后根据自己的需求设置合适的样式;不过更好的方式是通过这样的思路,在项目中做一些改进(比如平滑降级)等处理。

接下来是一个简单的使用模板。

<body>
 <script src="slide-unlock/core.js"></script>
 <script>
  const slider = new SlideUnlock()
  slider.init()
 </script>
</body>

你可以在 这里 看见完整的使用方式和效果。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery 多级下拉菜单核心代码
May 21 Javascript
JQuery FlexiGrid的asp.net完美解决方案 dotNetFlexGrid-.Net原生的异步表格控件
Sep 12 Javascript
js写一个弹出层并锁屏效果实现代码
Dec 07 Javascript
JQuery入门——移除绑定事件unbind方法概述及应用
Feb 05 Javascript
javascript 操作符(~、&amp;、|、^、)使用案例
Dec 31 Javascript
学习JavaScript设计模式之模板方法模式
Jan 20 Javascript
jQuery和hwSlider实现内容响应式可触控滑动切换效果附源码下载(二)
Jun 22 Javascript
javascript动画系列之模拟滚动条
Dec 13 Javascript
JS轮播图的实现方法
Aug 24 Javascript
你不知道的 TypeScript 高级类型(小结)
Aug 28 Javascript
vue用ant design中table表格,点击某行时触发的事件操作
Oct 28 Javascript
低门槛开发iOS、Android、小程序应用的前端框架详解
Oct 16 Javascript
微信小程序 自定义弹窗实现过程(附代码)
Dec 05 #Javascript
Nuxt v-bind绑定img src不显示的解决
Dec 05 #Javascript
微信小程序swiper左右扩展各显示一半代码实例
Dec 05 #Javascript
微信小程序顶部导航栏可滑动并选中放大
Dec 05 #Javascript
微信小程序实现点击按钮后修改颜色
Dec 05 #Javascript
React 父子组件通信的实现方法
Dec 05 #Javascript
Javascript中的this,bind和that使用实例
Dec 05 #Javascript
You might like
php初始化对象和析构函数的简单实例
2014/03/11 PHP
php简单获取文件扩展名的方法
2015/03/24 PHP
在WordPress中实现评论头像的自定义默认和延迟加载
2015/11/24 PHP
PHP实现一个简单url路由功能实例
2016/11/05 PHP
PHP连接MYSQL数据库的3种常用方法
2017/02/27 PHP
php给数组赋值的实例方法
2019/09/26 PHP
基于PHP的登录和注册的功能的实现
2020/08/06 PHP
silverlight线程与基于事件驱动javascript引擎(实现轨迹回放功能)
2011/08/09 Javascript
Dom 学习总结以及实例的使用介绍
2013/04/24 Javascript
js Date概念详细介绍
2013/11/22 Javascript
JavaScript中奇葩的假值示例应用
2014/03/11 Javascript
利用jquery动画特效和css打造的侧边弹出垂直导航
2014/04/04 Javascript
简单介绍JavaScript的变量和数据类型
2015/06/03 Javascript
jquery动感漂浮导航菜单代码分享
2020/04/15 Javascript
JS模拟酷狗音乐播放器收缩折叠关闭效果代码
2015/10/29 Javascript
angular.js之路由的选择方法
2016/09/24 Javascript
ECMAScript6 新特性范例大全
2017/03/24 Javascript
JS实现的获取银行卡号归属地及银行卡类型操作示例
2019/01/08 Javascript
VueX模块的具体使用(小白教程)
2020/06/05 Javascript
Python模块包中__init__.py文件功能分析
2016/06/14 Python
python数字图像处理之骨架提取与分水岭算法
2018/04/27 Python
Python使用pickle模块报错EOFError Ran out of input的解决方法
2018/08/16 Python
一行Python代码制作动态二维码的实现
2019/09/09 Python
pytorch 实现查看网络中的参数
2020/01/06 Python
Java Spring项目国际化(i18n)详细方法与实例
2020/03/20 Python
Python类super()及私有属性原理解析
2020/06/15 Python
浅谈Keras参数 input_shape、input_dim和input_length用法
2020/06/29 Python
一文弄懂Pytorch的DataLoader, DataSet, Sampler之间的关系
2020/07/03 Python
Ruby如何进行文件操作
2014/07/17 面试题
中专毕业自我鉴定
2013/10/16 职场文书
程序员岗位职责
2013/11/11 职场文书
企业申诉管理制度
2014/01/30 职场文书
怎样填写就业意向
2014/04/02 职场文书
关于运动会的广播稿(10篇)
2014/09/12 职场文书
慰问信模板
2015/02/14 职场文书
建房合同协议书
2016/03/21 职场文书