国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Drag&Drop 拖放API簡介以及在React中的實踐

lcodecorex / 939人閱讀

摘要:如其他屬性及方法,詳細可以查看跨終端能力跨終端能力是最大的特點。在指定區域的事件中,通過對象的屬性,即可獲得文件列表信息,如打印文件名在中實踐在項目中使用,依然遵循數據驅動的原則,即事件數據更新。同時,在事件中執行判斷。

最近有個需求,需要產品導航欄支持拖放。
雖然開源社區已有不少成熟的拖放庫,但考慮到代碼可控性和可定制性,還是自己寫吧。

選型

關于選型,前端實現拖放功能,無外乎幾種:
1、通過樣式布局+鼠標事件,采用此方案的插件如:@shopify/draggable
2、Canvas繪制,插件如:konva
3、Drag&Drop接口,插件如:dragula

經過一番研究,最終選擇了原生Drag&Drop的方案,原因如下:
1、原生拖放事件,順應JS語言發展趨勢;
2、兼容性符合項目要求;
3、在Can I use...中有如下描述:

最少的代碼,最方便的方法,就是它了。

事件

一個拖放行為,自然牽涉到兩部分元素,即拖動元素和釋放區域元素。
與之相關的事件總共有8個,其中綁定在拖動元素的事件有三個:dragdragstartdragend
剩下5個事件綁定在釋放區域元素上:dragenterdragoverdragleavedragexitdrop
具體定義可以參考mdn

定義可拖動元素

瀏覽器中,有三種元素,默認是可以被拖動的,它們是:
1、被選中后的文本;
2、圖片;
3、鏈接
其他元素要轉成可拖動元素,必須添加draggable="true",如:

"true">div>

注意:這里不能略寫,如寫成:

div>

是無效的。

定義可被釋放區域

要使一塊元素可被釋放,首先需要綁定dragenterdragover事件,然后阻止事件,如下:

"return false"> <div ondragover="event.preventDefault()">

因為,這兩個事件的默認行為就是“不觸發”drop事件,所以要定義成可被釋放區域,就反其道而行之即可。

DataTransfer對象

一個完整的拖放操作,除了拖動一個元素,在指定區域釋放之外,還有最重要的一步,就是將元素攜帶的信息在被釋放區域中展示。
比如,拖放一張圖片,本質上就是獲取到被拖動的圖片src屬性值,并在釋放時,在釋放區域展示一張相同src的圖片。
而這個信息,就存儲在DataTransfer對象中。
對于非默認可拖放元素來說,其包含的信息需要在dragstart事件中設置,使用DataTransfer.setData(),如:

dragItem.ondragstart = e => {
  e.dataTransfer.setData("text/plain", "drag info");
}

如果希望拖動時,展示自定義的圖片,還可以調用dataTransfer.setDragImage,如:

dragItem1.ondragstart = e => {
  const img = new Image(); 
  img.src = "img_url.jpg"; 
  e.dataTransfer.setDragImage(img, 0, 0);
}

drop事件中,可以取得拖放元素的信息,并將指定信息通過dom操作,展示在特定區域,如:

dropArea.ondrop = e => {
  e.preventDefault();
  const data = event.dataTransfer.getData("text/plain");
  const div = document.createElement("div");
  div.textContent = data;
  e.target.appendChild(div);
}

在DataTransfer對象還有一對屬性,用來確保釋放區域只能釋放特定類型的拖拽元素,即dropEffecteffectAllowed
effectAllowed只能在dragstart事件中設置,在dragenterdragover事件中,需要設置dropEffect的值與effectAllowed一致,才能觸發drop事件。如:

dragItem.ondragstart = e => {
  e.dataTransfer.effectAllowed = "move";
}

dropArea.ondragover = e => {
  e.preventDefault();
  e.dataTransfer.dropEffect = "move";
}

其他屬性及方法,詳細可以查看mdn

跨終端能力

跨終端能力是drag&drop最大的特點。
最常見的跨終端需求,就是從用戶的本地拖放文件到瀏覽器中指定區域實現上傳功能。
在指定區域的drop事件中,通過DataTransfer對象的files屬性,即可獲得文件列表信息,如:

dropArea.ondrop = e => {
  e.preventDefault();
  const files = e.dataTransfer.files;
  if (files.length) {
    Array.prototype.forEach.call(files, f => {
      console.log(f.name); //打印文件名
    });
  }
}
在React中實踐

在React項目中使用drag&drop,依然遵循React數據驅動的原則,即事件->數據->DOM更新
所以,像之前提到的,通過DataTransfer對象傳遞數據的方式,在React項目中,可以改為操作組件對象屬性,保證數據流的清晰。
但除此之外,在實際實踐中,還是遇到了一些問題,需要特殊處理。具體如下:

1、必須保留dataTransfer.setData

起初,為保證數據流清晰,在React組件中,綁定onDragStart,僅負責監聽事件,數據的變動和傳遞全部修改組件屬性,但是會遇到Firefox瀏覽器無法拖放的兼容問題。經查發現,在Firefox中,可拖放元素必須滿足:
1、添加draggable="true"
2、綁定事件dragstart
3、在dragstart中,dataTransfer.setData設置數據
所以,即使e.dataTransfer.setData("text", "");設置空字符串,也必須添加上這一條。

2、防止跨終端拖拽或不合法拖拽

drop&drag跨終端能力有時也會成為干擾。在項目中,會發現,如果沒有做判斷,同一個頁面同時打開兩個瀏覽器tab,其拖放元素可以跨tab拖動,可能會造成意外BUG。為此,需要增加判斷。
一種方式,在組件實例構建時,生成一個隨機字符,借助dataTransfer.setData,為拖放元素打上標記。同時,在drop事件中執行判斷。
當然,如果拖放元素和釋放區域分屬不同組件,則需要在他們的父組件中,生成隨機字符,以props形式,傳遞到兩個子組件。

3、防止Firefox自動打開新頁面

在上述提到的為拖放元素打標簽中,起初采用的是這樣的寫法:

e.dataTransfer.setData("text", uniqDataTransferTag);

結果在Firefox中,每次drop事件觸發時,瀏覽器會自動打開新tab并搜索uniqDataTransferTag(隨機字符)
根據官方解釋,需要在drop事件中調用e.preventDefault(),同時阻止冒泡e.stopPropagation(),但經過嘗試,依然不生效。初步判斷,可能與React的SyntheticEvent機制有關。于是只好曲線救國,改為設置自定義的MIME type,如:

e.dataTransfer.setData("ucloud_drag_tag", uniqDataTransferTag);
4、節流與避免event被回收

在項目中,需要在onDragOver中,判斷被拖放元素當前位置,并執行DOM操作。
根據定義,dragover事件會在被拖放元素拖到釋放區域上時,每幾百毫秒觸發一次,顯然不做任何處理會非常影響性能。這里,自然想到采用節流throttle方式優化。
由于節流是異步操作,而根據React的SyntheticEvent,event對象會在當前事件循環結束后移除,除非調用e.persist(),才能在異步操作中訪問到。

5、HACK拖放元素拖動過程中,實現“被拖走”的視覺效果

根據設計師要求,項目中希望實現元素拖動開始后要被拖走,如下圖:

但默認的拖放效果,其實是這樣:
很可惜,官方并沒有提供對被拖放元素拖動開始后設置效果的接口。經過嘗試,找到一個通過樣式HACK方法,如下:
1、新增一個css class,包含樣式:

transform: translateX(-9999px);

2、對被拖放元素添加樣式:

transition: transform 0.1s;

3。在拖動開始后,添加上述第一步的css class

6、實現長按元素激活拖放效果

根據交互設計,需要實現長按元素一定時長后才可以觸發拖拽。
起初,采用的方案是,綁定鼠標事件mousedown,觸發setTimeout,達到固定時長后觸發state更新,改變拖放元素的draggable值。但實際測試中發現,這種方法存在一定的失敗率,即明明已經達到了長按的時長,依然不能拖放。而且,在Firefox中這個問題更加明顯。
推測,可能是draggable的更新偶爾會晚于dragstart事件,導致拖放失敗。
于是轉變思路,增設組件的屬性作為判斷標志,在mousedown事件中更新判斷標志,而draggable始終設為true。如下:

// mousedown事件處理函數
handleLongPress = e => {
    this.resetDragTimer(); // 清除定時器

    return (this.triggerDragTimer = setTimeout(() => {
      this.isMenuDraggable = true; // 判斷標志
    }, this.triggerDragInterval));
};

// dragstart事件處理函數
handleDragStart = e => {
    if (!this.isMenuDraggable) {
        e.preventDefault();
    } else {
      ...
    }
};
總結

Drag&Drop作為原生拖放API,可以用最少代碼實現拖放,看似“簡單”,實際并非如此。在實踐中,還是需要對官方接口定義,以及各瀏覽器差異有足夠了解,才能避免各種未知錯誤。而在React這類數據驅動的框架中運用時,如何處理事件監聽,同時又不打亂組件的數據流,還是需要好好設計一番。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/7280.html

相關文章

  • 使用 Drag and Drop 給Web應用提升交互體驗

    摘要:注意點在鼠標操作拖放期間,有一些事件可能觸發多次,比如和。可拖拽元素,建議使用,設定可拖拽元素的鼠標游標,提升交互。在中使用拖拽中使用可以直接綁定到組件上。 什么是 Drag and Drop (拖放)? 簡單來說,HTML5 提供了 Drag and Drop API,允許用戶用鼠標選中一個可拖動元素,移動鼠標拖放到一個可放置到元素的過程。 我相信每個人都或多或少接觸過拖放,比如瀏覽...

    legendmohe 評論0 收藏0
  • React-sortable-hoc 結合 hook 實現 Draggin 和 Droppin

    摘要:啟動項目教程最終的目的是構建一個帶有趣的應用程序來自,可以在視口周圍拖動。創建組件,添加樣式和數據為簡單起見,我們將在文件中編寫所有樣式。可以看出,就是在當前的外層包裹我們所需要實現的功能。現在已經知道如何在項目中實現拖放 翻譯:https://css-tricks.com/draggi... React 社區提供了許多的庫來實現拖放的功能,例如 react-dnd, react-b...

    molyzzx 評論0 收藏0
  • HTML5拖放API Drag and Drop

    摘要:此文研究中的拖放接口,提供各個屬性和方法的說明,解決拖放過程中的拖拽數據對象存儲和獲取問題。方法增加一個拖拽數據對象到屬性中,并返回增加的拖拽數據對象。若拖拽數據對象是文本字符串類型,通過回調函數獲取拖拽數據中的字符串數據。 此文研究Web API中的拖放接口,提供各個屬性和方法的說明,解決拖放過程中的拖拽數據對象存儲和獲取問題。 拖放API作用到兩個目標對象,分別是拖拽目標對象和放置...

    dantezhao 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<