1. 程式人生 > 實用技巧 >實現一個美化原生拖拽的draggable-polyfill

實現一個美化原生拖拽的draggable-polyfill

拖拽的實現

在html5還未普及之前,實現元素的拖拽還算是一件比較麻煩的事,大概思路就是監聽滑鼠移動相關事件,下面是虛擬碼

odiv.onmousedown = function(ev){
    //記錄起始位置
}
document.onmousemove = function(ev){
    //移動目標元素
}
document.onmouseup = function(ev){
    //取消滑鼠移動事件
}

html5新增了拖放draggable標準,拖拽就變得簡單了,只需要通過監聽元素的拖放事件就能實現各種拖放功能。

<divdraggable="true">drag me</div>
除了設定draggable="true"屬性外,預設情況下img、連結預設是可拖拽的

當然,設定draggable="true"元素僅僅是“可拖拽”,鬆手後就還原,如果需要拖拽到指定位置僅需要在drop記錄一下就行了

dropbox.addEventListener('drop',function(ev){
    dragbox.style.left = XX;
    dragbox.style.top = XX;
})

原生的拖拽

原生拖拽有很多優勢,大家可以自行去了解一下,就好比原生和模擬器的區別。

但是,稍微有點審美追求的肯定不會用原生預設的拖拽,因為實在是不怎麼好看。

下面先看看原生的拖拽效果

原生的拖拽效果

原生預設的拖拽效果是一個半透明的預覽圖(暫且稱為“幽靈”吧),如果拖拽的元素尺寸比較小的話,生成的預覽圖還可以接受,只是半透明而已,如下

如果大一點就無法接受了,“幽靈”就不只是半透明,而且還有從滑鼠位置向外擴散的漸變效果(真不知道是怎麼設計的,可能是為了效能考慮吧),反正就是很難看吧,如下

其次,還有一個效果是被拖拽的元素仍保持不動,只是被拖拽出來的“幽靈”在移動,如果背景複雜一點,那麼就完全分不清了

資源搜尋網站大全 https://www.renrenfan.com.cn 廣州VI設計公司https://www.houdianzi.com

自定義原生的拖拽

原生的拖拽基本上是無法自定義的,唯一可以更改的只有setDragImage方法,可以指定一張預覽圖來代替預設放入預覽圖,然而這種方式也很雞肋,自定義的預覽圖仍然和預設的效果一直,都是半透明的,其次,實時生成當前節點的預覽圖也是一件麻煩事。

另一條思路其實也比較容易,如下。

  1. 去除預設的預覽圖
  2. 複製一份當前目標元素,cloneObj
  3. 監聽拖拽事件,改變cloneObj的位置
  4. 拖拽結束移除cloneObj

下面均為虛擬碼,完整程式碼可在文章末尾檢視

1. 去除預設的預覽圖

雖然setDragImage比較雞肋,但是我們可以設定一張透明的圖片就可以實現去除預設的預覽圖的效果了

dragbox.addEventListener('dragstart', function (ev) {
    var img = new Image();
    img.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' %3E%3Cpath /%3E%3C/svg%3E";
    ev.dataTransfer.setDragImage(img, 0, 0);
}
普通元素設定draggable="true"後,Chrome可以直接拖拽,FireFox需要在dragstart設定ev.dataTransfer.setData('text','任意值')才行

2.複製一份當前目標元素,cloneObj

複製一份當前目標元素,並且設定position:fixed等屬性,可以懸浮在頁面之上,然後新增到body,如下

var cloneObj = this.cloneNode(true);
cloneObj.style = 'position:fixed;left:0;top:0;z-index:999;pointer-events:none;transform:translate3d( ' + left + 'px ,' + top + 'px,0);'
document.body.appendChild(cloneObj);

3.監聽拖拽事件,改變cloneObj的位置

拖拽元素時會觸發drag事件,和滑鼠移動事件類似,可以取到當前滑鼠位置(在拖拽過程中並不觸發mousemove事件),同時也能隱藏原目標

dragbox.addEventListener('drag', function (ev) {
    if(cloneObj){
        cloneObj.style.transform = 'translate3d( ' + left + 'px ,' + top + 'px,0)';
        dragbox.style.visibility = 'visible';
    }
})

Chrome確實是這樣,FireFox雖然也能觸發drag事件,然後裡面卻取不到滑鼠位置資訊(均為0),所以,我們只能把監聽放在dragover上,雖然不太完美,也是一個方法

document.addEventListener('dragover', function (ev) {
    if(cloneObj){
        cloneObj.style.transform = 'translate3d( ' + left + 'px ,' + top + 'px,0)';    
    }
})

4.拖拽結束移除cloneObj

拖拽結束移除cloneObj,並且還原原目標

oDiv.addEventListener('dragend', function (ev) {
    document.body.removeChild(cloneObj);
    cloneObj = null;
    dragbox.style.visibility = 'visible';
})

draggable-polyfill

根據以上思路,完成了一個draggable-polyfill,這個polyfill的作用很簡單,僅僅只是單純的美化原生的拖拽,去除了原有的半透明預覽圖,不改變原有邏輯,只要專案中用到了原生拖拽,都可以打上這個補丁。

專案地址https://github.com/XboxYan/draggable-polyfill

使用方式也很簡單,直接引用就可以了

<scriptsrc="./lib/draggable-polyfill.js"></script>

如果是工程化專案,也可以使用npm安裝

npmi draggable-polyfill

然後直接匯入用就可以了

importdraggable-polyfill;

更多示例可檢視http://xboxyan.codelabo.cn/draggable-polyfill/example/index.html

需要注意的一點是:由於只是複製了當前節點,所以如果你的樣式依賴於父級,那麼複製出來的樣式就會和原目標不一樣

.parent .dragbox{
    background:red
}
/*改為*/
.dragbox{
    background:red
}

其他技巧

可以在網上隨便找一個原生拖拽的案例,直接在控制檯貼上以上draggable-polyfill.js,就可以立即“試用”了,比如說這個例子,開啟控制檯貼上然後回車再看看拖拽效果(如下)。