JavaScript實(shí)現(xiàn)滑塊補(bǔ)圖驗(yàn)證碼效果
<div class="container"> <div class="pic"> <div class="gap"></div> <div class="verify-pic"></div> </div> <div class="slide"> <div class="btn"></div> </div> </div>
沒(méi)錯(cuò),全部手工繪制,不用任何圖片、svg、字體圖標(biāo)之流
pic
為背景圖片,里面放著gap
空白塊,verify-pic
被拖動(dòng)的驗(yàn)證圖
slide
是滑塊,btn
是小按鈕
樣式
首先初始化樣式和變量
:root { --btn-w: 40px; --btn-h: 24px; /* 按鈕偽元素 */ --btn-dot-w: 4px; /* 滑塊 */ --bar-h: 10px; /* 背景圖 */ --pic-w: 640px; --pic-h: 390px; --pic-src: url(https://i0.hdslb.com/bfs/vc/c13315f4c4195b342fd0d2795fd6c8b090a717bf.jpg); --radius: 8px; } * { margin: 0; padding: 0; }
這幾個(gè)樣式很大眾 沒(méi)什么可講的
.container { display: flex; position: relative; height: var(--pic-h); width: var(--pic-w); flex-flow: column wrap; justify-content: space-between; padding: 10px; } .pic { position: relative; background: var(--pic-src) no-repeat; width: 640px; height: 340px; } .slide { position: relative; width: 100%; height: var(--bar-h); background-color: #999; border-radius: 4px; }
.btn { position: absolute; left: 0; /* (按鈕高度 - 拖動(dòng)條高度) * -1 / 2 */ top: calc((var(--btn-h) - var(--bar-h)) * -1 / 2); width: var(--btn-w); height: var(--btn-h); background-color: #b5a37e; border-radius: 10px; cursor: pointer; }
這個(gè)滑塊按鈕,要想在垂直方向居中,就需要拿按鈕高度 - 滑動(dòng)條高度 / 2
但是為什么要 乘以 -1
呢??
因?yàn)?code>DOM坐標(biāo)系是第三象限,負(fù)值為向上
接下來(lái)是里面的倆小薯?xiàng)l了
.btn::after, .btn::before { content: ""; position: absolute; top: 50%; transform: translateY(-50%); left: calc(var(--btn-w) / 3 - var(--btn-dot-w) / 2); height: var(--bar-h); width: var(--btn-dot-w); background-color: #eee; }
這里我把整個(gè)滑塊分成三份,所以位置就是滑塊的三分之一,后面的小薯?xiàng)l除以2是為了居中
.btn::before { /* 偽元素在按鈕的 2/ 3處 并減去自己的一半用來(lái)居中 */ left: calc(var(--btn-w) / 3 * 2 - var(--btn-dot-w) / 2); }
第二個(gè)小薯?xiàng)l就是三分之二的位置即可
至此 樣式完成
操作邏輯 & 效果實(shí)現(xiàn)
const container = document.querySelector('.container'), pic = container.querySelector('.pic'), // 大圖 vPic = pic.querySelector('.verify-pic'), // 拖動(dòng)圖片 gap = pic.querySelector('.gap'), // 背景圖空白塊 btn = document.querySelector('.btn'); // 滑動(dòng)條按鈕 const pic_w = getStyle(pic, 'width'), pic_h = getStyle(pic, 'height'), cont_w = getStyle(container, 'width'), cont_h = getStyle(container, 'height'), vPic_w = getStyle(vPic, 'width'), vPic_h = getStyle(vPic, 'height'), btn_w = getStyle(btn, 'width'); const offset = 14; // 可偏移距離 function getRadom(min, max) { return Math.floor(min + Math.random() * (max - min)); } function getStyle(el, key) { return parseInt(getComputedStyle(el)[key]); }
先獲取DOM
以及設(shè)置配置
這里一定不能用offset
系列獲取矩形屬性,因?yàn)殡[藏的元素?zé)o法獲取
初始化位置
function setPos() { const w = pic_w / 2, h = pic_h / 2 - vPic_h; // 移動(dòng)空缺元素到右上部分 const left = getRadom(w, pic_w - vPic_w), top = getRadom(0, h); gap.style.transform = `translate(${left}px, ${top}px)`; vPic.style.backgroundPosition = `${-left}px ${-top}px`; return [left, top]; }
把滑塊和圖片 移動(dòng)到右上方隨機(jī)位置
返回值作為最終對(duì)比值
由于left
是指元素左邊的距離,所以要減去元素寬度
let x = 0, // 滑倒最后的值 moving = false; btn.addEventListener('mousedown', function (e) { moving = true; setShow(top, 'block'); }); btn.addEventListener('mouseup', function () { setShow(top, 'none'); }); function setShow(top, flag) { vPic.style.display = flag; vPic.style.transform = `translateY(${top}px)`; btn.style.transform = 'none'; vPic.style.transform = 'none'; x = 0; }
這里的setShow
可復(fù)用多次
現(xiàn)在有什么問(wèn)題嗎??
問(wèn)題大著呢,你把mouseup
綁在了小按鈕上,當(dāng)你抬起位置不是按鈕,就不能觸發(fā)了
正確做法是綁定在window
上
接下來(lái)是重點(diǎn),滑動(dòng)事件
window.addEventListener('mousemove', function (e) { if (!moving) { return; } // 圖片位置 = 鼠標(biāo)位置 - 滑動(dòng)條位置 - 按鈕 / 2 ----減去按鈕是居中 x = e.clientX - container.getBoundingClientRect().left - btn_w / 2; btn.style.transform = `translateX(${x}px)`; vPic.style.transform = `translate(${x}px, ${top}px)`; });
window.addEventListener('mousemove', function (e) { if (!moving) { return; } // 圖片位置 = 鼠標(biāo)位置 - 滑動(dòng)條位置 - 按鈕 / 2 ----減去按鈕是居中 x = e.clientX - container.getBoundingClientRect().left - btn_w / 2; btn.style.transform = `translateX(${x}px)`; vPic.style.transform = `translate(${x}px, ${top}px)`; });
這樣基本就實(shí)現(xiàn)了,但是沒(méi)有判斷邊界呢,現(xiàn)在可以隨意滑動(dòng)
function judge(x) { const { left } = container.getBoundingClientRect(); return ( x - left < 0 || x + left > cont_w + left ); } window.addEventListener('mousemove', function (e) { if (!moving || judge(e.clientX)) { return; } // 圖片位置 = 鼠標(biāo)位置 - 滑動(dòng)條位置 - 按鈕 / 2 ----減去按鈕是居中 x = e.clientX - container.getBoundingClientRect().left - btn_w / 2; btn.style.transform = `translateX(${x}px)`; vPic.style.transform = `translate(${x}px, ${top}px)`; });
當(dāng)x
軸小于0
或者鼠標(biāo)大于圖片寬度時(shí)退出
這時(shí)候給x
賦值,鼠標(biāo)抬起時(shí)判斷
window.addEventListener('mouseup', function () { const flag = x > left - offset && x < left + offset; moving = false; if (flag) { cb && cb(); } else { setShow(top, 'none'); } });
如果成功 執(zhí)行外面的回調(diào)函數(shù) 反之重置位置
現(xiàn)在基本完全實(shí)現(xiàn)
就在我喜出望外之際,我發(fā)現(xiàn)滑動(dòng)一些刁鉆的角度,會(huì)讓鼠標(biāo)屬性改變,變成這樣
cursor: not-allowed;
因?yàn)檫@個(gè)原因,會(huì)擾亂事件
但是我整篇代碼也沒(méi)設(shè)置過(guò)
所以我冥思苦想
那一定是事件默認(rèn)行為搞的鬼
所以我給每個(gè)元素阻止了默認(rèn)行為,最終排查發(fā)現(xiàn),是mousedown
導(dǎo)致的
btn.addEventListener('mousedown', function (e) { // 不阻止默認(rèn)行為 會(huì)導(dǎo)致鼠標(biāo)屬性變成 `now-allowed` e.preventDefault(); moving = true; setShow(top, 'block'); });
以上就是JavaScript實(shí)現(xiàn)滑塊補(bǔ)圖驗(yàn)證碼效果的詳細(xì)內(nèi)容,更多關(guān)于JavaScript滑塊補(bǔ)圖驗(yàn)證碼的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript中創(chuàng)建對(duì)象的幾種方法總結(jié)
以下幾種,是javascript中最常用的創(chuàng)建對(duì)象的方式。初學(xué)者看到后,可能會(huì)暈掉,甚至?xí)X(jué)得擔(dān)心。其實(shí)完全不用擔(dān)心,這些種方式,只需要掌握一兩種,對(duì)其他的幾種只需要理解就好了2013-11-11基于cornerstone.js的dicom醫(yī)學(xué)影像查看瀏覽功能
這篇文章主要介紹了基于cornerstone.js的dicom醫(yī)學(xué)影像查看瀏覽功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07JS實(shí)現(xiàn)超炫網(wǎng)頁(yè)煙花動(dòng)畫(huà)效果的方法
這篇文章主要介紹了JS實(shí)現(xiàn)超炫網(wǎng)頁(yè)煙花動(dòng)畫(huà)效果的方法,實(shí)例分析了javascript實(shí)現(xiàn)煙花動(dòng)畫(huà)效果的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03詳解JavaScript中Hash Map映射結(jié)構(gòu)的實(shí)現(xiàn)
Hash Map通常在JavaScript中作為一個(gè)簡(jiǎn)單的來(lái)存儲(chǔ)鍵值對(duì)的地方,不過(guò)哈希對(duì)象Object并不是一個(gè)真正的哈希映射,沒(méi)Java中的Hash Map來(lái)的那么強(qiáng)大,well,接下來(lái)帶大家詳解JavaScript中Hash Map映射結(jié)構(gòu)的實(shí)現(xiàn)2016-05-05ES6 迭代器(Iterator)和 for.of循環(huán)使用方法學(xué)習(xí)(總結(jié))
這篇文章主要介紹了ES6 迭代器(Iterator)和 for.of循環(huán)使用方法學(xué)習(xí)總結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02Bootstrap模態(tài)對(duì)話框用法簡(jiǎn)單示例
這篇文章主要介紹了Bootstrap模態(tài)對(duì)話框用法,結(jié)合實(shí)例形式分析了Bootstrap模態(tài)對(duì)話框的簡(jiǎn)單創(chuàng)建與使用操作技巧,需要的朋友可以參考下2018-08-08JavaScript關(guān)閉當(dāng)前頁(yè)面(窗口)不帶任何提示
這篇文章主要介紹了JavaScript關(guān)閉當(dāng)前頁(yè)面(窗口)不帶任何提示的具體實(shí)現(xiàn),需要的朋友可以參考下2014-03-03將JSON字符串轉(zhuǎn)換成Map對(duì)象的方法
下面小編就為大家?guī)?lái)一篇將JSON字符串轉(zhuǎn)換成Map對(duì)象的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11高性能WEB開(kāi)發(fā) flush讓頁(yè)面分塊,逐步呈現(xiàn) flush讓頁(yè)面分塊,逐步呈現(xiàn)
在處理比較耗時(shí)的請(qǐng)求的時(shí)候,我們總希望先讓用戶先看到部分內(nèi)容,讓用戶知道系統(tǒng)正在進(jìn)行處理,而不是無(wú)響應(yīng)。2010-06-06