這次來做一個拖動排序,帶有動畫效果,先上效果圖,不熟悉拖動api的可以查看鏈接
在線演示:http://jsrun.net/XpiKp/edit
思路(先不考慮動畫)
每個li既是可拖動,同時也是容器,拖動到li上面時移動拖動的li。
先把html寫好
<ul>
<li class="ele" draggable="true">1</li>
<li class="ele" draggable="true">2</li>
<li class="ele" draggable="true">3</li>
<li class="ele" draggable="true">4</li>
<li class="ele" draggable="true">5</li>
<li class="ele" draggable="true">6</li>
<li class="ele" draggable="true">7</li>
<li class="ele" draggable="true">8</li>
</ul>
css
ul {
list-style: none;
margin: 200px;
font-size: 0;
}
.ele {
font-size: 16px;
width: 100px;
height: 40px;
border: 1px solid #999;
background: #EA6E59;
margin: 2px 0;
border-radius: 10px;
padding-left: 10px;
color: white;
cursor: move;
}
下面添加事件,由于拖動是實時的,所以沒有使用drop而是使用了dragover。并且用一個變量來保存當前拖動的元素。這里直接使用事件委托,直接使用ul來監聽
var node = document.querySelector("#container");
var draging = null;
//使用事件委托,將li的事件委托給ul
node.ondragstart = function(event) {
/firefox設置了setData后元素才能拖動?。。。? event.dataTransfer.setData("te", event.target.innerText); //不能使用text,firefox會打開新tab
draging = event.target;
}
node.ondragover = function(event) {
event.preventDefault();
var target = event.target;
//因為dragover會發生在ul上,所以要判斷是不是li
if (target.nodeName === "LI"&&target !== draging) {
//_index是實現的獲取index
if (_index(draging) < _index(target)) {
target.parentNode.insertBefore(draging, target.nextSibling);
} else {
target.parentNode.insertBefore(draging, target);
}
}
}
接下來就是移動正在拖動的li了,當前li的index大于容器li時就插入在容器的前面,反之插入在容器的后面,那么怎么獲取當前li的index呢?Node有一個屬性previousElementSibling,表示該元素前面的一個元素,那么我們可以這樣
function _index(el) {
var index = 0;
if (!el || !el.parentNode) {
return -1;
}
while (el && (el = el.previousElementSibling)) {
index++;
}
return index;
}
好的,基本的排序就做好了,在線演示地址http://jsrun.net/GkiKp/edit
動畫
好了,先講思路,動畫其實不需要給所有元素都添加,只需要給移動的元素和目標元素添加動畫就夠了,例如我們交換了1和2的位置,那么就給1和2添加動畫。這種添加動畫的思路是通用的,比如可以做元素的頁面進入動畫。
我們可以記錄1和2交換之前的位置,然后獲取交換之后的位置,然后讓元素從原位置慢慢移動到目標位置。
圖片表示
那么我們先獲取初始位置,肯定是在移動元素之前進行獲取,那么我們在dragover移動元素之前加上
var targetRect = target.getBoundingClientRect();
var dragingRect = draging.getBoundingClientRect();
然后在交換位置之后再次獲取元素位置
var targetAfter = target.getBoundingClientRect();
var dragingAfter = draging.getBoundingClientRect();
位置還原
先獲取兩者的差,然后就可以為其添加transform,由于我們需要將元素瞬間移動到原來的位置,所以我們需要將其transition設為none
target.style.transition='none';
target.style.transform='translate3d(' +
(targetRect.left - targetAfter.left) + 'px,' +
(targetRect.top - targetAfter.top) + 'px,0)'
//draging同理
然后就可以再設置transform進行移動,這里如果直接設置的話,當然動畫是無法顯示的,原因可以看這里
target.offsetWidth; //觸發重繪
target.style.transition='all 300ms';
target.style.transform='translate3d(0,0,0)';
還有一個問題
由于我們的動畫是添加在dragover里面,然而dragover是會不停的觸發,加過就是元素不?!俺榇ぁ?,所以我們需要判斷元素是否已經添加了動畫。這個的話方法就很多了,這里我是設置了一個定時器,事件到了之后將transition和transform清空,設置動畫之前進行判斷定時器是否已經存在
clearTimeout(target.animated);
target.animated = setTimeout(function() {
target.style.transition='';
target.style.transform='';
target.animated = false;
//draging同理
}, ms);