成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久

您的位置:首頁技術(shù)文章
文章詳情頁

JavaScript 實(shí)現(xiàn)拖拽效果組件功能(兼容移動端)

瀏覽:169日期:2023-10-08 16:38:33

頁面元素拖拽是一種非常實(shí)用的前端效果,基于元素拖拽可以實(shí)現(xiàn)很多不同的功能,增加客戶端許多操作的便捷性,大大提高用戶體驗(yàn)。日常生活中大家多多少少都見過這種效果,所以就不廢話了,直接開干吧。

預(yù)期目標(biāo)

實(shí)現(xiàn)一個(gè) Class 類,通過該 Class,可以將任意 DOM 元素(比如 div)一鍵變?yōu)榭赏献顟B(tài),也可以恢復(fù)成原來的狀態(tài),例如這樣:

<!DOCTYPE html><html lang='en'><head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <title>Document</title> <style> #box1 { height: 50px; width: 50px; background-color: cadetblue; } #box2 { height: 50px; width: 50px; background-color: blue; } #box3 { height: 50px; width: 50px; background-color: red; } </style></head><body> <div id='box1'>1</div> <a id='box2'>2</a> <div id='box3'>3</div></body><script type='module'>// 我們要完成的目標(biāo) Class import DragElement from ’./DragElement.js’ // 使 3 個(gè)元素可拖拽 let box1 = new DragElement(document.querySelector('#box1')) let box2 = new DragElement(document.querySelector('#box2')) let box3 = new DragElement(document.querySelector('#box3')) // box2 解除拖拽效果,恢復(fù)為原來的樣子 // box2.dragRelease()</script></html>

原本的樣子

JavaScript 實(shí)現(xiàn)拖拽效果組件功能(兼容移動端)

隨意拖放

JavaScript 實(shí)現(xiàn)拖拽效果組件功能(兼容移動端)

一、算法思路

1.1 拖拽的行為描述

我們先思考如何描述拖拽這一行為。我的思路是這樣的:

先對拖拽這一行為進(jìn)行定義:在指定的元素上,若保持鼠標(biāo)按下狀態(tài),則該元素將會跟隨鼠標(biāo)移動。當(dāng)鼠標(biāo)松開,該元素將不再跟隨鼠標(biāo)移動。如果是移動端的話,鼠標(biāo)的角色改為觸摸(touch)即可。

根據(jù)定義,我們可以確定幾個(gè)關(guān)鍵信息:

鼠標(biāo)移動,是拖拽算法本身的作用范圍。 鼠標(biāo)按下,開啟拖拽 鼠標(biāo)松開,關(guān)閉拖拽

可以看到,完整的拖拽功能分為 3 個(gè)部分,分別是開啟、運(yùn)行與關(guān)閉。分別對應(yīng)鼠標(biāo)的按下、運(yùn)行、松開事件。 因此我們至少需要設(shè)計(jì)相應(yīng)的 3 個(gè)函數(shù),作為事件的回調(diào)。在這里我分別命名為 dragStart()、dragMoving()、dragEnd()。

這里就出現(xiàn)了第一個(gè)重點(diǎn):如何描述拖拽功能的狀態(tài)變化?

顯然,鼠標(biāo)的按下與松開,將會決定DOM 元素是否能夠被拖拽,這是一種 “狀態(tài)” 的變化。這種狀態(tài)的變化,在編碼上,可以通過一個(gè)變量來實(shí)現(xiàn),也可以通過不斷地添加 or 移除回調(diào)函數(shù)來實(shí)現(xiàn)。如果通過變量的話,在鼠標(biāo)沒有按下時(shí),鼠標(biāo)移動事件也會觸發(fā)進(jìn)行狀態(tài)判斷,這其實(shí)是沒有必要的,因此方案上我們選擇后者,鼠標(biāo)按下與松開時(shí),分別添加和移除實(shí)現(xiàn)拖拽的函數(shù)。

以上是拖拽本身的行為,此外,由于我們需要 DOM 元素能夠在原本的狀態(tài)和可拖拽狀態(tài)之間進(jìn)行轉(zhuǎn)換,因此我們還需要 2 個(gè)函數(shù),一個(gè)用于將 DOM 元素變?yōu)榭赏献顟B(tài),另一個(gè)用于卸載這些狀態(tài)。前者我稱為 dragActive(),后者我稱為 dragRelease()。它們做的事情,就是添加和解除事件監(jiān)聽。

現(xiàn)在第一個(gè)問題解決了,我們來解決第二個(gè)問題,那就是:拖拽函數(shù)怎么實(shí)現(xiàn)?

1.2 拖拽的實(shí)現(xiàn)

首先看核心的,拖拽本身應(yīng)該怎么計(jì)算,如何讓元素跟著鼠標(biāo)走。

同樣的,我們繼續(xù)想象實(shí)際的場景。鼠標(biāo)按下時(shí),我們假設(shè)鼠標(biāo)的坐標(biāo)處于(x0, y0) 點(diǎn),鼠標(biāo)移動,假設(shè)移動到了(x1, y1) 點(diǎn)。那么該元素,相對自身初始位置便移動了(x1-x0, y1-y0) 的距離。這種相對于自身移動的,在 CSS 上可以通過相對定位,也可以通過 transform: translate 或 translate3d 來實(shí)現(xiàn),由于定位在布局中很常用,我們也不知道指定的 DOM 元素到底是什么樣式,為了盡量不影響原來的布局,所以我們采用 transform。

再回到具體計(jì)算上,鼠標(biāo)的位置 x 和 y,可以通過事件回調(diào)函數(shù)傳入的參數(shù) event 得到,在 PC 端是 event.clientX 和 event.clientY,移動端是 event.changedTouches[0].pageX 和 event.changedTouches[0].pageY。而 mousemove 事件是連續(xù)觸發(fā)的,我們的拖動也要讓元素跟著鼠標(biāo)連續(xù)運(yùn)動,因此需要不停更新 (x0, y0),(x1, y1) 的值,在每個(gè)細(xì)小的運(yùn)動中都進(jìn)行差值計(jì)算,就像微積分一樣。為了方便記錄和更新,我們不妨把拖動中需要的變量用一個(gè)對象表示,稱為 dragInfo,掛載到 document 元素上,這樣在不同的函數(shù)、對象之間都可以訪問。

class DragElement { constructor(element) { this.element = element document.dragInfo = { element: this.element, x0: 0, y0: 0, x1: 0, y1: 0 } }}

element 表示拖拽的元素,x 和 y 分別為計(jì)算所需的變量。

獲取鼠標(biāo)位置的函數(shù):

updateDragPosition = (event) => {return {x: event.clientX || (event.changedTouches ? event.changedTouches[0].pageX : document.dragInfo.x0),y: event.clientY || (event.changedTouches ? event.changedTouches[0].pageY : document.dragInfo.y0)}}

或許有人會有疑問,為啥不直接 event.clientX || event.changedTouches[0].pageX,而是要用三元表達(dá)式。這是因?yàn)橛行┣闆r下,上述兩者可能都不存在,比如當(dāng)鼠標(biāo)移到瀏覽器左邊緣的時(shí)候,就無法獲得位置:

JavaScript 實(shí)現(xiàn)拖拽效果組件功能(兼容移動端)

獲取鼠標(biāo)位置的函數(shù)寫完后,就可以寫拖拽的函數(shù)了:

dragMoving = (event) => {document.dragInfo.x1 = this.updateDragPosition(event).x - document.dragInfo.x0 + document.dragInfo.x1document.dragInfo.y1 = this.updateDragPosition(event).y - document.dragInfo.y0 + document.dragInfo.y1document.dragInfo.x0 = this.updateDragPosition(event).xdocument.dragInfo.y0 = this.updateDragPosition(event).ydocument.dragInfo.element.style.transform = ’translate3d(’ + document.dragInfo.x1 + ’px, ’ + document.dragInfo.y1 + ’px, 0)’;}

但此時(shí)問題就來了,由于 document 上只有一個(gè) dragInfo,不同的組件之間坐標(biāo)沖突如何解決?其實(shí)這個(gè)簡單,只需要在 this.element 上添加一個(gè)對象記錄每次拖拽后的位置即可,每當(dāng)點(diǎn)擊一個(gè)拖拽元素時(shí),就將該元素的信息注入 document.dragInfo。

this.element.dragPostion = {x: 0,y: 0}

綜上,我們已經(jīng)解決了最核心的流程描述與算法部分,接下來只要編碼就可以了。

二、編碼實(shí)現(xiàn)

請根據(jù)之前說的思路,自行閱讀代碼,整體邏輯還是非常清晰的,如果有一些細(xì)節(jié)不懂,可以在評論區(qū)提出,或者我有空了再補(bǔ)充。

class DragElement { constructor(element) { this.element = element document.dragInfo = { element: this.element, x0: 0, y0: 0, x1: 0, y1: 0 } document.updateDragPosition = this.updateDragPosition this.dragActive() } // 更新鼠標(biāo)位置 updateDragPosition = (event) => { return { x: event.clientX || (event.changedTouches ? event.changedTouches[0].pageX : document.dragInfo.x0), y: event.clientY || (event.changedTouches ? event.changedTouches[0].pageY : document.dragInfo.y0) } } // 為元素配置相應(yīng)的拖拽控制函數(shù) dragActive = () => { if (!this.element) return this.element.style.display = 'block' this.element.addEventListener(’mousedown’, this.dragStart, false) this.element.addEventListener(’touchstart’, this.dragStart, false) this.element.addEventListener(’mouseup’, this.dragEnd, false) // 釋放 this.element.addEventListener(’touchend’, this.dragEnd, false) this.element.addEventListener(’touchcancel’, this.dragEnd, false) // 為該元素添加一個(gè)對象,保存當(dāng)前位置 this.element.dragPostion = { x: 0, y: 0 } } // 釋放配置 dragRelease = () => { this.element.removeEventListener(’mousedown’, this.dragStart) this.element.removeEventListener(’touchstart’, this.dragStart) this.element.removeEventListener(’mouseup’, this.dragEnd) // 釋放 this.element.removeEventListener(’touchend’, this.dragEnd) this.element.removeEventListener(’touchcancel’, this.dragEnd) this.element.style.display = '' return this.element } // 點(diǎn)擊捕獲拖拽元素,初始化相應(yīng)信息 dragStart = (event) => { document.dragInfo.element = this.element document.dragInfo.x0 = this.updateDragPosition(event).x document.dragInfo.y0 = this.updateDragPosition(event).y document.dragInfo.x1 = this.element.dragPostion.x document.dragInfo.y1 = this.element.dragPostion.y // 屏蔽默認(rèn)行為 event.preventDefault(); // mousemove 綁定在 document 上,防止鼠標(biāo)過快可能導(dǎo)致的元素跟丟 document.addEventListener(’mousemove’, this.dragMoving, false) document.addEventListener(’touchmove’, this.dragMoving, false) } // 實(shí)時(shí)計(jì)算、更新相對位置變化 dragMoving = (event) => { document.dragInfo.x1 = this.updateDragPosition(event).x - document.dragInfo.x0 + document.dragInfo.x1 document.dragInfo.y1 = this.updateDragPosition(event).y - document.dragInfo.y0 + document.dragInfo.y1 document.dragInfo.x0 = this.updateDragPosition(event).x document.dragInfo.y0 = this.updateDragPosition(event).y document.dragInfo.element.style.transform = ’translate3d(’ + document.dragInfo.x1 + ’px, ’ + document.dragInfo.y1 + ’px, 0)’; } // 關(guān)閉拖拽 dragEnd = () => { // 保存當(dāng)前位置 this.element.dragPostion.x = document.dragInfo.x1 this.element.dragPostion.y = document.dragInfo.y1 // 解綁 document.removeEventListener(’touchmove’, this.dragMoving) document.removeEventListener(’mousemove’, this.dragMoving) }}export default DragElement

到此這篇關(guān)于JavaScript 實(shí)現(xiàn)拖拽效果組件功能(兼容移動端)的文章就介紹到這了,更多相關(guān)JavaScript 拖拽效果組件內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: JavaScript
相關(guān)文章:
成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久
国产一区高清视频| 狠久久av成人天堂| 欧美激情1区2区| 91麻豆精品91久久久久久清纯| 亚洲成人av福利| 今天的高清视频免费播放成人| 久久久欧美精品sm网站| 成人av网站在线| 日韩视频永久免费| 精品一区二区三区在线观看国产| 老牛影视一区二区三区| 亚洲第一综合色| 伊人激情综合| 日本一区二区在线不卡| 99久久精品免费看国产 | 337p亚洲精品色噜噜| 六月婷婷色综合| 欧美揉bbbbb揉bbbbb| 美国欧美日韩国产在线播放 | 久久日一线二线三线suv| 丰满少妇久久久久久久| 91超碰这里只有精品国产| 捆绑调教一区二区三区| 欧美日韩一二三| 激情文学综合网| 欧美三级在线播放| 久久国产三级精品| 欧美人妇做爰xxxⅹ性高电影| 久久成人18免费观看| 欧美视频一区二区在线观看| 久久99国产精品麻豆| 欧美日本一区二区三区| 国产精品一区久久久久| 日韩午夜在线观看| 91视频免费看| 中文字幕精品一区二区三区精品| 欧美日韩一区二区三区四区在线观看| 国产精品久久久久久久久久免费看 | 久久aⅴ国产紧身牛仔裤| 五月天精品一区二区三区| 日本道在线观看一区二区| 久久99国产精品久久99果冻传媒| 在线播放中文字幕一区| www.欧美色图| 中文字幕一区二区三区乱码在线| 国产一区二区三区高清| 三级欧美在线一区| 欧美群妇大交群中文字幕| 国产精品1区二区.| 日韩免费成人网| 女人香蕉久久**毛片精品| 中文字幕亚洲电影| 翔田千里一区二区| 裸体歌舞表演一区二区| 精品欧美一区二区久久| 欧美精品自拍| 亚洲精品乱码久久久久久久久 | 狠狠久久婷婷| 五月婷婷久久丁香| 色欧美日韩亚洲| 国产sm精品调教视频网站| 欧美激情中文字幕一区二区| 亚洲免费精品| 久久精品72免费观看| 久久亚洲春色中文字幕久久久| 很黄很黄激情成人| 午夜欧美大尺度福利影院在线看| 欧美午夜精品免费| 国产成人精品网址| 国产精品麻豆欧美日韩ww| 亚洲一区二区在线看| 国产在线国偷精品产拍免费yy| 久久久天堂av| 国产深夜精品| 久久精品国产99国产精品| 337p粉嫩大胆色噜噜噜噜亚洲| 亚洲国产精品一区制服丝袜 | 秋霞午夜av一区二区三区| 精品国产青草久久久久福利| 亚洲午夜精品久久久久久app| 视频一区欧美日韩| 精品久久人人做人人爽| 亚洲毛片网站| 国产精品一级在线| 亚洲欧美影音先锋| 欧美日韩成人激情| 欧美色一级片| 亚洲成人激情av| 精品区一区二区| 国产精品久久久久久模特| 韩国一区二区三区| 国产精品久久久一本精品| 欧美午夜不卡视频| 国内精品久久久久久久97牛牛| 视频在线在亚洲| 日韩视频一区二区三区| 99成人免费视频| 国产成都精品91一区二区三| 一区二区三区91| 精品日韩在线观看| 久久先锋资源| 欧美不卡高清| 日本亚洲一区二区| 欧美国产欧美亚州国产日韩mv天天看完整| 可以看av的网站久久看| 成人av电影在线观看| 午夜免费久久看| 欧美极品另类videosde| 欧美私模裸体表演在线观看| 欧美日韩一卡| 国产一区视频网站| 亚洲狼人国产精品| 欧美成人一区二区三区| 一本一道久久a久久精品| 欧美视频二区| 国产精品一区二区91| 亚洲一二三四久久| 精品国产免费一区二区三区香蕉| 一本色道久久综合亚洲精品按摩| 欧美黄免费看| 精彩视频一区二区| 亚洲日本一区二区| 久久一留热品黄| 欧美肥妇free| 另类av一区二区| 亚洲午夜久久久久久尤物| 成人国产电影网| 精品一区二区av| 日韩专区中文字幕一区二区| 国产精品丝袜一区| 日韩一区二区三| 欧洲一区在线电影| 国内视频一区| 成人av影院在线| 精品一区二区在线播放| 亚洲成av人影院在线观看网| 国产精品污网站| 欧美成人免费网站| 欧美日韩精品欧美日韩精品一| 亚洲在线黄色| 国内一区二区在线视频观看| av激情综合网| 国产一区福利在线| 日韩av午夜在线观看| 一区二区在线观看免费| 亚洲国产高清不卡| 精品av久久707| 9191久久久久久久久久久| 色妞www精品视频| 亚洲尤物影院| 激情视频一区二区三区| 99re视频精品| 成人av网址在线观看| 国产精品1区2区3区在线观看| 免费成人在线观看| 亚洲成人动漫在线观看| 亚洲黄色小视频| 亚洲人成精品久久久久| 国产欧美一区视频| 日韩女优毛片在线| 8v天堂国产在线一区二区| 欧美色偷偷大香| 91国产丝袜在线播放| 亚洲一区二区高清视频| 亚洲精品乱码久久久久久蜜桃麻豆| 欧美激情91| 亚洲欧美影院| 91首页免费视频| 91麻豆高清视频| 91美女在线看| 97久久精品人人做人人爽| jizz一区二区| aaa欧美色吧激情视频| 99免费精品视频| 成人一区二区三区中文字幕| 国产乱码精品1区2区3区| 九一久久久久久| 久久国产婷婷国产香蕉| 久久草av在线| 蜜臂av日日欢夜夜爽一区| 热久久免费视频| 免费成人性网站| 美女视频网站久久| 毛片一区二区三区| 激情综合五月婷婷| 国产激情一区二区三区桃花岛亚洲| 国产成人午夜精品影院观看视频| 成人免费毛片app| 91麻豆123| 在线国产日韩| 午夜一级在线看亚洲| 午夜综合激情| 久久婷婷国产综合尤物精品| 日本精品视频一区二区| 欧美亚洲综合一区| 欧美人成免费网站| 欧美tk—视频vk| 久久精品免视看| 成人欧美一区二区三区1314| 亚洲午夜精品17c|