本文介绍了原生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 端正常的工作了,不过在移动端却并不理想。
为了它能够在移动端也可以很好的工作,我们可以借助 touchstart
、touchmove
、touchend
等事件来完成。
绑定这些事件的时机和处理方式和之前的三个事件分别相对应,所以我们新增两个方法(和 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
事件中获取 clientX
、clientY
时不能在事件对象上直接读取,而是在 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="/UploadFiles/2021-04-02/core.js">你可以在 这里 看见完整的使用方式和效果。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]