自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

關(guān)于前端里的拖拖拽拽,了解一下?

開發(fā) 前端
最近在開發(fā)一款低代碼平臺,所以借此機(jī)會分享一下關(guān)于“拖拽”這一交互的基礎(chǔ)知識和實(shí)踐經(jīng)驗(yàn),希望可以給有需要的同學(xué)提供一點(diǎn)參考。

最近在項(xiàng)目中使用了 react-dnd[1],一個(gè)基于 HTML5 的拖拽庫,“拖拽能力”豐富了前端的交互方式,基于拖拽能力,會擴(kuò)展各種各樣的拖拽反饋效果,因此有必要學(xué)習(xí)了解,最好的學(xué)習(xí)方式就是實(shí)操!

拖拽交互常見于各種前端編輯器里,而“編輯器”是一個(gè)集成前端技術(shù)能力的綜合性工程,其中就會涉及到各種形式的拖拽交互,因?yàn)椤巴献А笔翘嵘脩趔w驗(yàn)的重要交互方式,所以需要對拖拽的交互效果做各種定制化,作為開發(fā)者理應(yīng)熟練掌握“拖拽”的使用!

最近在開發(fā)一款低代碼平臺,所以借此機(jī)會分享一下關(guān)于“拖拽”這一交互的基礎(chǔ)知識和實(shí)踐經(jīng)驗(yàn),希望可以給有需要的同學(xué)提供一點(diǎn)參考。

一、HTML5 中的拖放

拖(Drag)和放(Drop)是 HTML5 標(biāo)準(zhǔn)的組成部分,了解掌握之后,舉一反三,有助于提升我們在拖拽場景下技術(shù)方案的設(shè)計(jì)能力。

1.1 draggable 屬性

現(xiàn)代瀏覽器中,不難發(fā)現(xiàn),圖片標(biāo)簽()是可以被長按拖拽,但如果需要自定義的 DOM 節(jié)點(diǎn)可以被拖拽需要配置以告訴瀏覽器提供對元素(Element / Tag)支持拖拽的能力。

而元素是否允許被拖放且可響應(yīng) API 操作依賴于 draggable[2] 全局標(biāo)簽屬性

draggable 是一個(gè)布爾值類型的標(biāo)簽屬性:

  • true:元素可被拖拽
  • false:元素不可拖拽

當(dāng)元素設(shè)置了 draggable 屬性,此時(shí)長按就可以自由拖拽了:

1.2 Darg & Drop 事件

HTML 的 drag & drop 使用了“DOM Event”和從“Mouse Event”繼承而來的“drag event” 。

一個(gè)典型的拖拽操作: 用戶選中一個(gè)可拖拽的(draggable)元素,并將其拖拽(鼠標(biāo)按住不放)至一個(gè)可放置的(droppable)元素上,然后松開鼠標(biāo)。

在拖動元素期間,一些與拖放相關(guān)的事件會被觸發(fā),像 drag 和 dragover 類型的事件會被頻繁觸發(fā)。

除了定義拖拽事件類型,每個(gè)事件類型還賦予了對應(yīng)的事件處理器

各個(gè)事件的時(shí)機(jī)可以用下面這個(gè)圖簡單表示:

??注意: dragOver 事件的默認(rèn)行為是:“Reset the current drag operation to "none"”。也就是說,如果不阻止放置元素的 dragOver 事件,則放置元素不會響應(yīng)“拖動元素”的“放置行為”

// 讓綁定該事件的元素支持放置
function handleDragOver(e) {
// 阻止默認(rèn)的重置行為
// 即可成為拖拽元素的放置區(qū)
e.preventDefault();
}


從設(shè)計(jì)事件標(biāo)準(zhǔn)來看,如果我們需要自行實(shí)現(xiàn)拖拽的效果,就需要從這關(guān)鍵的幾個(gè)事件去思考設(shè)計(jì)。

1.3 DataTransfer

在上述的事件類型中,不難發(fā)現(xiàn),放置元素和拖動元素分別綁定了自己的事件,可如何將拖拽元素和放置元素建立聯(lián)系以及傳遞數(shù)據(jù)?

這就涉及到 DataTransfer 對象:

DataTransfer 對象用于保存拖動并放下(drag and drop)過程中的數(shù)據(jù)。它可以保存一項(xiàng)或多項(xiàng)數(shù)據(jù),這些數(shù)據(jù)項(xiàng)可以是一種或者多種數(shù)據(jù)類型?!?DataTransfer - MDN[3]

DataTransfer 對象在不同瀏覽器上因?yàn)闃?biāo)準(zhǔn)可能不一樣使得 API 有差異,但有幾個(gè)“標(biāo)準(zhǔn)(常用)”屬性和方法需要熟悉

在 Chrome 瀏覽器上的 DataTransfer 實(shí)例如下:

(1) 屬性

(2) 方法

在簡單的拖拽場景中,其實(shí)可以類比 window.localStorage 對象的 setItem() 和 getItem() 方法來理解記憶.

但 getData() 在測試中發(fā)現(xiàn)只能在 ondrop 事件中獲取到值:

1.4 一個(gè)案例掌握拖放 API

拖動元素 放置區(qū)域

<div>
<div class="drag" draggable="true" id="dragger" ondragstart="handleDragStart(event)">拖動元素</div>
<div class="drop" ondrop="handleDrop(event)" ondragover="allowDrop(event)">放置區(qū)域</div>
</div>
<script>
function handleDragStart(e) {
e.dataTransfer.setData('DRAG_NODE_ID', e.target.id)
}
function handleDragOver(e) {
e.preventDefault();
}
function handleDrop(e) {
e.preventDefault();
var data = e.dataTransfer.getData('DRAG_NODE_ID');
e.target.appendChild(document.getElementById(data));
}
</script>


演示案例: https://codepen.io/DYBOY/pen/eYeyvWm

效果:

演示

拖拽演示效果

1.6 兼容性

是 HTML5 標(biāo)準(zhǔn)提出的能力,因此各大瀏覽器廠商對于標(biāo)準(zhǔn)的支持有差異,其兼容性參考如下:

相較于傳統(tǒng)的通過鼠標(biāo)事件:mousedown、mousemove、mouseup 組合實(shí)現(xiàn)的拖拽要簡單很多,少了放入目標(biāo)邊界的判斷,也少了對位置的實(shí)時(shí)獲取操作。

另外目前的 API 不算多,例如我們想要定制化拖拽的圖片大小、鼠標(biāo)樣式等,目前暫時(shí)沒發(fā)現(xiàn)比較方便的解決方式,但是從另一個(gè)角度來說,讓我們對于拖拽能力的設(shè)計(jì)和標(biāo)準(zhǔn)有了一個(gè)更深切的認(rèn)識,對于設(shè)計(jì)實(shí)現(xiàn)拖拽交互有了一個(gè)“理論”基礎(chǔ)!

二、手搓一個(gè)

有了上面的基礎(chǔ)知識,那么實(shí)現(xiàn)一個(gè)列表拖拽排序并不是什么難事。

2.1 設(shè)計(jì)實(shí)現(xiàn)

結(jié)合上述的 Drag & Drop 的事件類型,那么拖拽排序主要是針對“拖動對象”之間相互作用關(guān)系的邏輯梳理,此處我們暫且區(qū)分為:

  • 源對象: 拖拽列表中被拖動的單個(gè)列表項(xiàng)
  • 目標(biāo)對象: 拖拽列表中和“源對象”產(chǎn)生“相互作用”的列表項(xiàng)

整體的交互事件的設(shè)計(jì)思路如下:

(1) ondragstart

此時(shí)開始拖拽“源對象”的時(shí)機(jī),在此事件回調(diào)函數(shù)中改變“源對象”的樣式,設(shè)置拖拽的一些傳遞參數(shù)等初始值。

// 源對象開始拖拽
const handleDragStart = (e: React.DragEvent<HTMLDivElement>) => {
e.dataTransfer.effectAllowed = "move";
setDragId(e.currentTarget.dataset.index); //dataset 獲取拖拽項(xiàng)的 id
};


(2) ondragover

正與拖拽中的“源對象”產(chǎn)生相互影響的目標(biāo)對象,此時(shí)“源對象”處于“目標(biāo)對象”的正上方,目標(biāo)對象 100ms/次的頻率調(diào)用“目標(biāo)對象”的 ondragover 中聲明的回調(diào)事件。

此時(shí),我們會計(jì)算改變“源對象”和“目標(biāo)對象”的位置。

// 源對象在目標(biāo)對象上方時(shí)
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault(); // 允許放置,阻止默認(rèn)事件
const dropId = e.currentTarget.dataset.index;
move(dragId, dropId); // 改變原列表數(shù)據(jù)
};


(3) ondrag

該事件作用于“源對象”,此時(shí)正處于拖拽過程中,此時(shí)可以改變源對象的 opacity、display(none)、visiblity 樣式屬性,如果在 dragstart 事件改變,則會導(dǎo)致拖拽拷貝對象丟失。

// 源對象被拖拽過程中
const handleDrag = (e: React.DragEvent<HTMLDivElement>) => {
e.currentTarget.style.opacity = "0";
};


(4) ondragend

在松手完成“源對象”的放置時(shí),主動調(diào)用綁定在“源對象”身上的事件,此時(shí)恢復(fù)更改的樣式。

// 源對象被放置完成時(shí)
const handleDragEnd = (e: React.DragEvent<HTMLDivElement>) => {
e.currentTarget.style.opacity = "1";
};


2.2 實(shí)現(xiàn)效果

2.3 加點(diǎn)動畫

上面的實(shí)現(xiàn)中效果還算可以,但是少了拖拽項(xiàng)的切換過程動畫,直接在 dragover 事件中通過 move(dragId, dropId) 方法直接修改了原列表數(shù)據(jù)的排序,導(dǎo)致切換突變。

借助 animation 新增 CSS 幀動畫:

@keyframes dropUp {
100% {
transform: translateY(5px);
}
}
@keyframes dropDown {
100% {
transform: translateY(-5px);
}
}

.drop-up{
animation: dropUp 0.3s ease-in-out forwards;
}
.drop-down{
animation: dropDown 0.3s ease-in-out forwards;
}


同樣的在 dragOver 事件中處理,新增邏輯代碼:

// 源對象在目標(biāo)對象上方時(shí)
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
...
// 設(shè)置動畫
const dropId = e.currentTarget.dataset.index;
const dragIndex = findIndex(listData, (i) => i.id === dragId);
const dropIndex = findIndex(listData, (i) => i.id === dropId);
// 通過增加對應(yīng)的 CSS class,實(shí)現(xiàn)視覺上的動畫過渡
e.currentTarget.classList.remove("drop-up", "drop-down");
if (dragIndex < dropIndex) {
e.currentTarget.classList.add("drop-down");
} else if (dragIndex > dropIndex) {
e.currentTarget.classList.add("drop-up");
}
...
};


增加了動畫的效果:

增加了動畫的效果

看起來似乎好一點(diǎn)了,當(dāng)然大家可以去擴(kuò)充動畫的效果,亦或者借助三方動畫庫。

三、已有拖拽庫

目前主流的拖拽庫有:

  • react-dnd: https://github.com/react-dnd/react-dnd/
  • react-beautiful-dnd: https://github.com/atlassian/react-beautiful-dnd/
  • sortablejs: https://sortablejs.github.io/Sortable/
  • react-sortable-hoc: https://github.com/clauderic/react-sortable-hoc/

關(guān)于幾者的差異,可以參閱:《關(guān)于react中使用拖拽插件的評測[4]》

四、總結(jié)

由于低代碼平臺其實(shí)會有豐富的拖拽場景,從可擴(kuò)展和兼容性上考慮,最終選擇了 react-dnd 作為基礎(chǔ)拖拽庫,當(dāng)然,在復(fù)雜的拖拽場景下,是需要自行擴(kuò)展該拖拽庫,上手難度相對會高一點(diǎn),不過有了這些“拖拽知識”作為前置基礎(chǔ),那么擴(kuò)展功能也就不是什么難事了。

參考資料

[1]react-dnd - Github: https://react-dnd.github.io/react-dnd/about

[2]draggable - MDN: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable

[3]DataTransfer - MDN: https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer

[4]關(guān)于react中使用拖拽插件的評測: https://juejin.cn/post/6956112150989373448

責(zé)任編輯:姜華 來源: DYBOY
相關(guān)推薦

2020-02-10 14:26:10

GitHub代碼倉庫

2020-12-10 08:44:35

WebSocket輪詢Comet

2022-03-24 13:36:18

Java悲觀鎖樂觀鎖

2020-03-01 17:53:38

Excel大數(shù)據(jù)微軟

2019-02-20 14:16:43

2018-06-05 17:40:36

人工智能語音識別

2024-04-11 12:19:01

Rust數(shù)據(jù)類型

2018-04-25 06:46:52

2019-03-11 14:33:21

Redis內(nèi)存模型數(shù)據(jù)庫

2023-03-02 08:00:55

包管理工具pnpm 包

2022-03-07 06:34:22

CQRS數(shù)據(jù)庫數(shù)據(jù)模型

2021-07-06 14:56:20

深度學(xué)習(xí)編程人工智能

2018-07-17 14:42:50

2023-11-18 09:09:08

GNUBSD協(xié)議

2024-02-28 18:22:13

AI處理器

2021-05-12 10:59:39

Kubernetes容器集群

2018-03-21 09:08:06

超融合架構(gòu)本質(zhì)

2022-03-10 07:39:33

.NET部署模式

2023-04-27 13:32:16

AutoGPTAPI智能

2022-09-23 15:01:33

圖片加載代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號