您好,登錄后才能下訂單哦!
本文介紹了原生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="slide-unlock/core.js"></script> <script> const slider = new SlideUnlock() slider.init() </script> </body>
你可以在 這里 看見完整的使用方式和效果。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。