1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        動(dòng)態(tài)列表組件 - 拖拽排序功能設(shè)計(jì)與實(shí)現(xiàn)

        共 5951字,需瀏覽 12分鐘

         ·

        2022-02-19 20:41

        術(shù)??堅(jiān)??

        最終效果

        原生實(shí)現(xiàn)原理

        關(guān)于拖拽

        標(biāo)簽的圖片默認(rèn)是可以拖動(dòng)的(效果如上圖)

        然而其他的標(biāo)簽(div等)是不能被拖動(dòng)的,鼠標(biāo)點(diǎn)擊選擇后移動(dòng)沒(méi)有拖拽效果,需要添加屬性draggable="true" 使得元素可以被拖動(dòng)。

        拖放過(guò)程觸發(fā)事件

        • 在拖動(dòng)目標(biāo)上觸發(fā)事件(源元素) :

          • ondragstart[1]- 用戶開始拖動(dòng)元素時(shí)觸發(fā)
        • ondrag[2]- 元素正在拖動(dòng)時(shí)觸發(fā)
        • ondragend[3]- 用戶完成元素拖動(dòng)后觸發(fā)
        • 釋放目標(biāo)時(shí)觸發(fā)的事件:

          • ondragenter - 當(dāng)被鼠標(biāo)拖動(dòng)的對(duì)象進(jìn)入其容器范圍內(nèi)時(shí)觸發(fā)此事件
        • ondragover[4]- 當(dāng)某被拖動(dòng)的對(duì)象在另一對(duì)象容器范圍內(nèi)拖動(dòng)時(shí)觸發(fā)此事件
        • ondragleave[5]- 當(dāng)被鼠標(biāo)拖動(dòng)的對(duì)象離開其容器范圍內(nèi)時(shí)觸發(fā)此事件
        • ondrop[6]- 在一個(gè)拖動(dòng)過(guò)程中,釋放鼠標(biāo)鍵時(shí)觸發(fā)此事件

        ps.需要注意是 ondragend 和 ondragover 的默認(rèn)事件 Reset the current drag operation to "none".也就是說(shuō)它的默認(rèn)行是當(dāng)ondragover觸發(fā)時(shí),移動(dòng)會(huì)失效(產(chǎn)生殘影),所以想讓一個(gè)元素可放置,需要阻止默認(rèn)行為。

        element.ondragover?=?event?=>?{??????
        ????event.preventDefault();?????//?...?
        }

        一個(gè)簡(jiǎn)單js原生拖拽demo

        思考思路

        image.png

        ondragstart

        獲得源元素

        ondrag

        進(jìn)行拖拽過(guò)程中的源元素樣式改變

        ondragend

        移除多余樣式,拿到ondragover中最后獲得的目標(biāo)元素index,改變列表數(shù)組

        ondragover

        以較短時(shí)間內(nèi)生成快照,記錄源元素的拖動(dòng)軌跡經(jīng)過(guò)了哪些元素,用來(lái)生成最終的目標(biāo)元素,給途經(jīng)元素增加位移動(dòng)畫樣式

        問(wèn)題解決

        如何進(jìn)行數(shù)據(jù)傳遞

        思路1:為每個(gè)item設(shè)置className=index${index},通過(guò)Number(event.target.classList.slice("5"))來(lái)取值,

        可以達(dá)到目標(biāo)效果,但實(shí)現(xiàn)不太優(yōu)雅,所以放棄這個(gè)思路

        思路2:為每個(gè)item設(shè)置屬性data-index={index},通過(guò)event.target.dataset.index來(lái)取值,符合代碼的語(yǔ)義性,故采用這個(gè)方法

        嵌套DOM造成ondragover重復(fù)觸發(fā)

        ondragover函數(shù)會(huì)在源元素移動(dòng)到目標(biāo)元素范圍內(nèi)且不停止拖拽時(shí)反復(fù)觸發(fā),如果源元素非單層DOM結(jié)構(gòu)時(shí),每一層DOM都會(huì)觸發(fā)ondragover,造成數(shù)據(jù)混亂

        下圖控制臺(tái)第一個(gè)紅框的數(shù)據(jù)是拖拽item1時(shí)打印,第二個(gè)紅框是拖拽item2時(shí)打印,可以發(fā)現(xiàn)item1的兩層DOM結(jié)構(gòu)觸發(fā)了兩次打印:

        html>

        <html?lang="zh">
        ??<head>
        ????<meta?charset="UTF-8"?/>

        ????<meta?name="viewport"?content="width=device-width,?initial-scale=1.0"?/>

        ????<meta?http-equiv="X-UA-Compatible"?content="ie=edge"?/>

        ????<title>Documenttitle>

        ????<style>
        ??????.wrapper?{
        ????????display:?flex;
        ????????border:?1px?solid?orangered;
        ????????padding:?10px;
        ??????}

        ??????.col?{
        ????????border:?1px?solid?#808080;
        ????????height:?500px;
        ????????width:?200px;
        ????????margin:?0?10px;
        ????????padding:?10px;
        ??????}

        ??????.item?{
        ????????border:?1px?solid?#808080;
        ????????margin:?5px?0;
        ??????}
        ????
        style>
        ??head>

        ??<body>
        ????<div?class="wrapper">
        ??????<div?class="col1?col">
        ????????<div?class="item"?id="item1"?draggable="true">
        ??????????<div>item1div>
        ????????div>

        ????????<div?class="item"?id="item2"?draggable="true">item2div>
        ??????div>
        ????div>

        ????<script>
        ??????let?cols?=?document.getElementsByClassName("col");

        ??????for?(let?col?of?cols)?{
        ????????col.ondragover?=?(e)?=>?{
        ??????????e.preventDefault();

        ??????????console.log(e.target);
        ????????};
        ??????}
        ????
        script>
        ??body>
        html>

        解決這個(gè)問(wèn)題的方法就是放棄使用e.target,改用e.currentTarget

        target和currentTarget的概念:

        1、target發(fā)生在事件流的目標(biāo)階段,而currentTarget發(fā)生在事件流的整個(gè)階段(捕獲、目標(biāo)和冒泡階段)

        2、只有當(dāng)目標(biāo)流處于目標(biāo)階段的時(shí)候才相同。

        3、而當(dāng)事件流處于捕獲和冒泡階段時(shí),target指向被點(diǎn)擊的對(duì)象,而currentTarget指向當(dāng)前事件活動(dòng)的對(duì)象,通常是事件的祖元素。

        拖拽結(jié)束后出現(xiàn)重影

        所謂重影就是我們?cè)谕蟿?dòng)到指定位置后,頁(yè)面我們拖動(dòng)的元素先慢慢漂移到了初始位置,隨后才更新到了我們指定的位置

        屏幕錄制2021-03-15 下午9.gif

        解決方法是在上層父元素上添加方法

        onDragOver``={(e:) => {

        e.``preventDefault``();

        }}>

        阻止默認(rèn)的禁止拖拽行為(慢慢漂移到了初始位置就是禁止默認(rèn)拖拽行為的體現(xiàn))

        合適的拖拽過(guò)渡動(dòng)畫

        讓畫面出現(xiàn)一個(gè)上移或下移"讓位"的效果

        .drag-up?{
        ??animation:?dragup?ease?0.1s?1;

        ??animation-fill-mode:?forwards;
        }

        .drag-down?{
        ??animation:?dragdown?ease?0.1s?1;

        ??animation-fill-mode:?forwards;
        }

        @keyframes?dragup?{
        ??from?{
        ????margin-top:?10px;
        ??}

        ??to?{
        ????margin-top:?50px;
        ??}
        }

        @keyframes?dragdown?{
        ??from?{
        ????margin-bottom:?10px;

        ????margin-top:?50px;
        ??}

        ??to?{
        ????margin-bottom:?50px;

        ????margin-top:?10px;
        ??}
        }

        其他第三方庫(kù)

        名稱react-dndreact-beautiful-dndreact-sortable-hoc
        包體積較大大(>100k)較小
        TS支持無(wú)無(wú)
        維護(hù)情況良好良好一年以上未曾更新
        傳參方式event.target.datasetevent.target.datasetref
        使用難度學(xué)習(xí)成本較高學(xué)習(xí)成本較低學(xué)習(xí)成本較低
        核心實(shí)現(xiàn)html5 drag APIhtml5 drag APIhtml5 mouse API
        倉(cāng)庫(kù)地址https://github.com/react-dnd/react-dndhttps://github.com/atlassian/react-beautiful-dndhttps://github.com/clauderic/react-sortable-hoc/issues

        其實(shí)除了最后一個(gè)庫(kù),其他兩個(gè)比較著名的庫(kù)體積都不是很小,所以簡(jiǎn)單需求原生實(shí)現(xiàn)還是有必要的

        react-dnd

        https://codesandbox.io/s/github/react-dnd/react-dnd/tree/gh-pages/examples_hooks_js/01-dustbin/single-target?from-embed=&file=/src/Box.jsx:437-440(體驗(yàn)demo)

        React DnD建立在 HTML5 drag and drop API[7]之上。因?yàn)樗梢詫?duì)已拖動(dòng)的DOM節(jié)點(diǎn)進(jìn)行屏幕快照,并將其用作“拖動(dòng)預(yù)覽”,這樣你就不必在光標(biāo)移動(dòng)時(shí)進(jìn)行任何繪制相關(guān)的操作,同時(shí)這個(gè)API也是處理文件刪除事件的唯一方法。

        但是,HTML5拖放API也有一些缺點(diǎn)。它在觸摸屏上不起作用,并且與其他瀏覽器相比,它在IE上提供的定制化更少。

        這就是為什么在React DnD中以可插入方式實(shí)現(xiàn)HTML5 drag and drop API的原因。你不必非得用它,你完全可以根據(jù)原生的觸摸事件,鼠標(biāo)事件等來(lái)封裝自己的實(shí)現(xiàn)。這種可插拔的實(shí)現(xiàn)在React DnD中稱為Backends。該庫(kù)現(xiàn)在只在HTML backend中使用,未來(lái)可能會(huì)有更多地方會(huì)用到。

        backends的作用類似于React的綜合事件系統(tǒng):它們都是把瀏覽器差異抽象出來(lái)并處理本地的DOM事件。不同的是,Backends并不依賴React或者React的綜合事件系統(tǒng)。底層實(shí)現(xiàn)中,backends做的就是原生的Dom事件轉(zhuǎn)換成React DnD能處理的內(nèi)部 Reduxactions。

        react-beautiful-dnd

        https://react-beautiful-dnd.netlify.app/?path=/story/single-vertical-list--basic(體驗(yàn)demo)

        react-sortable-hoc

        http://clauderic.github.io/react-sortable-hoc/#/react-virtualized/elements-of-varying-heights?_k=dbj6xa(體驗(yàn)demo)

        核心代碼

        const?[dragged,?setDragged]?=?useState<any>();

        const?[over,?setOver]?=?useState<any>();

        const?[draggable,?setDraggable]?=?useState(false);

        const?dragStart?=?(e:?any)?=>?{
        ??e.currentTarget.style.backgroundColor?=?"#fafafa";

        ??setDragged(e.currentTarget);
        };

        const?dragEnd?=?(e:?any)?=>?{
        ??e.preventDefault();

        ??e.target.style.display?=?"flex";

        ??e.target.classList.remove("drag-up");

        ??over.classList.remove("drag-up");

        ??e.target.classList.remove("drag-down");

        ??over.classList.remove("drag-down");

        ??const?from?=?cloneDeep(value[dragged.dataset.index]);

        ??const?to?=?cloneDeep(value[over.dataset.index]);

        ??splice(dragged.dataset.index,?1,?to);

        ??splice(over.dataset.index,?1,?from);

        ??e.target.style.opacity?=?"1";

        ??e.target.style.backgroundColor?=?"";
        };

        const?dragOver?=?(e:?any)?=>?{
        ??e.preventDefault();

        ??const?dgIndex?=?dragged.dataset.index;

        ??const?taIndex?=?e.currentTarget.dataset.index;

        ??const?animateName?=?dgIndex?>?taIndex???"drag-up"?:?"drag-down";

        ??if?(over?&&?e.currentTarget.dataset.index?!==?over.dataset.index)?{
        ????over.classList.remove("drag-up",?"drag-down");
        ??}

        ??if?(!e.currentTarget.classList.contains(animateName))?{
        ????e.currentTarget.classList.add(animateName);

        ????setOver(e.currentTarget);
        ??}
        };

        {items.map((item,?index)?=>?{
        ??return?(???draggable={draggable}
        ???data-index={index}
        ???onDragStart={e?=>?{
        ?????dragStart(e);
        ???}}
        ???onDrag={(e:?any)?=>?{
        ?????e.preventDefault();
        ?????e.target.style.opacity?=?'0';
        ???}}
        ???onDragOver={dragOver}
        ???onDragEnd={e?=>?{
        ?????dragEnd(e);
        ???}}>
        //你的列表數(shù)組數(shù)據(jù)???
        )
        }

        參考資料

        [1]

        ondragstart: https://www.runoob.com/jsref/event-ondragstart.html

        [2]

        ondrag: https://www.runoob.com/jsref/event-ondrag.html

        [3]

        ondragend: https://www.runoob.com/jsref/event-ondragend.html

        [4]

        ondragover: https://www.runoob.com/jsref/event-ondragover.html

        [5]

        ondragleave: https://www.runoob.com/jsref/event-ondragleave.html

        [6]

        ondrop: https://www.runoob.com/jsref/event-ondrop.html

        [7]

        HTML5 drag and drop API: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API

        ???

        便內(nèi),對(duì)^_^

        ?點(diǎn)、?~。

        H5-Dooring, 讓H5制作, 更簡(jiǎn)單

        瀏覽 77
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产精品色呦呦| 色综合天天综合网国产成人网| 日韩在线你懂的| 91久久人澡人妻人人澡人人爽 | 在线视频一区二区| 中文字幕在线视频日本| 天天夜夜狠狠| 91精品国产闺蜜国产在线闺蜜| 亚洲韩国中文字幕| 久久久影院| 国产传媒自拍| 亚洲成人视频免费观看| 伊人久久国产| 俺也去五月婷婷| 在线观看免费视频黄| 伊人大香蕉视频在线观看| 天天日天天噜| www.91超碰在线| 中文字幕永久在线视频v1.0| 成人AV电影在线观看| 日韩性爱视频| 欧美日韩国产在线观看| 久久久久久久国产精品| 91亚洲在线| 99涩涩| 成人无码一区二区三区| 69久久久| 韩国中文无码| 99看片| 各种BBwBBwBBwBBw| 激情无码精品| 囯产精品一区二区三区AV做线 | 午夜爱爱免费视频| 色情片在线播放| 国产三级黄色片| 亚洲国产精品一区二区三区| 老司机狠狠干| AV在线观看黄| 日日干干| 国产三级黄色视频| 日本免费爱爱视频| 日韩人妻精品无码久久| 亚洲综合免费观看| 无码一区二区三区在线| 四虎成人视频| 青青操逼视频| 99久久精| 中文字幕精品亚洲熟女| 人人搞人人摸| 国产精品成人99一区无码| 日韩无码免费看| 少妇搡BBBB搡BBB搡造水多| 国产在线观看免费| 福利在线| 中文无码日本一级A片人| 亚洲天堂日本| 欧美成人性爱图片| 熟女少妇一区二区三区| 欧美性高潮| 中文资源在线a中文| 欧美日韩免费在线| 2026国产精品视频| 狠狠干狠狠爱| 日本人妻在线视频| 日韩三级AV| 亚洲爆乳无码一区二区三区| 欧美综合第一页| 国产高清视频在线播放| 三级午夜在线无码| 亚洲区欧美区| 国产免费a片| 亚洲精品色图| 色视频在线| 国精产品一二四区黑人| A级片在线观看| 人妻久久久| 成人三级片在线| 国产调教视频| 西西444WWW大胆无视频软件亮点| 囯产精品久久久久久久久久| 狠狠亚洲| 你懂的视频在线观看| 不卡在线视频| 欧美激情一区二区| 91在线无码精品秘国产三年| av一区二区三区| 人妻少妇av中文字幕乱码牛牛| 激情淫荡少妇| caobi999| 日韩无码精品AV| 人人草人人看人人摸| 人人操人人爱人人摸| 91人妻人人人| 久久b| 欧美黄色免费在线观看| 国产成人黄色电影| 日韩激情一区| 日韩无码视频网| 黄色无码电影| 狠狠干2021| 黄色小网站在线观看| 不卡的av| 中文字幕视频在线| 91成人免费视频| 撸一撸成人在线做爱视频。| 欧美亚洲成人精品| 国产精品无码激情视频| 国产精品黄视频| 国产无码午夜| 99免费小视频| 欧洲一区在线观看| 国产97在线观看| 国产麻豆三级片| 影音先锋国产| 日皮视频免费观看| 在线观看国产视频| 亚洲vs无码秘蜜桃少妇小说| 理论三级片| 蜜芽av在线观看| 人人上人人操| 青娱乐成人电影| 免费69视频看片| 69视频免费观看| 无码人妻系列| 日韩午夜精品| 激情五月综合| 国产欧美一区二区三区视频| 自拍毛片| 成年人免费网站| 中国丰满妇BBwBBwHD| 五月激情六月| 在线黄色AV| 超碰97久久| 色婷婷导航| 91香蕉视频| 亚洲男女av| 国内精品人妻无码久久久影院蜜桃| 波多野吉衣av| 午夜黄色视频在线观看| 在线观看日韩av| 人妻二区| 国产av天堂| 亚洲一级二级| 日韩精品一区二区三免费视频| 成人免费一级视频| 亚洲丰满熟妇| 黄色电影A| 久久久久久穴| 人妻骚逼| 99精品视频免费观看| 黑人vs亚洲人在线播放| 国产尤物在线| 国产伦精品一级A片视频夜夜| 高清无码不卡在线观看| 国产精品成人在线观看| 中文无码日本一级A片久久影视| 玉米地一级婬片A片| 视频一区乳奴| 欧美自拍一区| 特级西西人体www高清大胆| 97人妻人人澡人人爽人人| 欧美日韩国产成人在线| 无码视频网| 欧美A级视频| 色老板在线视频| A级片免费看| 亚洲AV成人无码精品区| 毛片毛片毛片毛片毛片| 青草精品视频| 久久久久99精品成人片欧美一区| 中国女人如毛片| 加勒比国产在线| 午夜福利播放| 你懂的在线网站| 国产有码视频| 人人操碰成人网| 手机无码在线播放| 久色99| 成人高清无码视频| 国产在线黄片| 精品视频在线观看| 国产精品国内自产| 欧美成人手机在线看片| 天堂俺去俺来也www久久婷婷| 中文字幕第6页| 亚洲人妻视频| 久草视频在线免费| 久草大香蕉视频| 在线日韩AV| 另类罕见稀奇videos| 国产码在线成人网站| 久99在线视频| 操逼逼片| 97人妻天天摸天天爽天天| 久射久| 国产操逼视频| 美女十八禁| 婷婷高清无码| 国产区AV| 日韩福利视频| 69堂在线观看| 亚洲日逼网| 久久久黄色视频| 久久综合中文字幕| 免费精品黄色网页| 潮喷AV| 亚洲性爱视频| 色色视频网站| 亚洲欧洲久久电影| 伊人网导航| 91精品成人| 免费一级黄| 黄色国产视频| 久久久偷拍视频| 日本精品黄色| 国产一区二区视频在线观看| 女人操逼视频| 欧美熟妇擦BBBB擦BBBB| 久99久视频| 自拍偷拍成人视频| 久久草成人网| 俺去也av| 欧美日韩免费在线| 国产一级a爱做片免费☆观看| 免费看日韩毛片| 91久久人澡人妻人人澡人人爽 | 日韩免费高清无码| 大香蕉在线免| 色中色AV| 京东一热本色道久久爱| 超碰久热| 国产一区免费观看| 又黄又爽视频| 韩日无码视频| 国产精品的电影| re久久| 国产精品成人无码专区| 中文字幕中文字幕| 中字无码AV| 亚洲三级电影在线观看| 婷婷色在线视频| 国产av一区二区三区| 91丨精品丨国产丨丝袜| 翔田千里50岁无码| 国产一区免费| 午夜偷拍网站| 亚洲无码婷婷| 蜜桃av在线播放| 欧洲天堂在线视频网站| 在线免费观看成人网站| 久久婷婷综合网| 国产精品A片| 欧美特黄AAAAAA| 天堂网AV在线| 狠狠撸视频| 伊人影院麻豆| 日本操B视频| 婷婷久久综合久色综| xxx久久| 亚洲乱码一区二区三区| 天天添天天操| 91亚洲国产成人| 羽月希奶水饱胀在线播放| 久草加勒比| 久久精品禁一区二区三区四区五区 | 蜜臀AV在线观看| 久久亚洲av| 色呦呦一欧美| 国产AⅤ无码一区二区| 精品久久精品| 国产浮力草草| 一区二区三级片| 蜜桃传媒一区二区亚洲| 波多野结衣无码流出| 亚洲一区翔田千里无码| 黄色一级片视频| 在线观看视频日韩| 亚洲天堂在线看| 99热免费在线| 麻豆少妇| 久久久成人免费视频| 欧美不卡在线| 怡红院麻豆| 五月丁香狠狠爱| 日韩人妻精品中文字幕免费| 欧一美一婬一伦一区二区三区自慰国 | 豆花视频免费| 99久久精| 蝌蚪九色啦403| 操逼爽| 色五月婷婷中文字幕| 亚洲砖区| 极品久久| 一级片在线| 少妇搡BBBB搡BBB搡小说 | 五月天在线观看| 免费AV网站| 日本老女人视频| 成人乱无码AV在线观看| 国产精品日韩欧美|