<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>拖拽排序</title>
<style>
*{padding: 0;margin: 0;}
li{list-style: none;}
.drag-wrap{
position: relative;
width: 500px;
}
.drag-wrap .drag-item{
transition: unset;
z-index: 2;
}
.drag-wrap li{
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.15);
position: absolute;
width: 90px;
margin: 5px;
height: 60px;
user-select: none;
background-color: blueviolet;
transition: left 0.3s ease-in-out,
top 0.3s ease-in-out;
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<ul class="drag-wrap">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
</ul>
<script>
var dragSort = {
// 容器元素
el: null,
// 节点宽度
nodeWidth: 100,
// 节点高度
nodeHeight: 70,
// 每行节点个数
nodeNumber: 5,
// 容器下所有卡片的节点
nodeList: [],
// 记录拖拽中,被拖拽节点的序号;防止重复排序
index: 0,
init(el) {
this.el = el;
// 获取容器下所有卡片的节点
this.nodeList = [...this.el.children];
// 给所有节点初始化序号,绑定事件
this.nodeList.forEach((node,index)=> {
node.sort = index;
node.addEventListener('mousedown', this.addDragEvent.bind(this));
});
// 进行第一次排序
this.sortNodePosition(this.nodeList);
},
/**
* 给每个节点进行一次排序
* @param {Array} 元素节点数组
*/
sortNodePosition(nodeList) {
nodeList.forEach(node => {
node.style.left = node.sort % this.nodeNumber * this.nodeWidth + 'px';
node.style.top = Math.floor(node.sort / this.nodeNumber) * this.nodeHeight + 'px';
})
},
/**
* 绑定拖拽事件
* @param {Object} e 事件对象
*/
addDragEvent(e) {
// 当前拖拽的节点
let { target } = e;
// 增加class,取消动效,防止拖拽掩饰;增加zIndex层级
target.className = 'drag-item';
let styleLeft = parseInt(target.style.left),
styleTop = parseInt(target.style.top);
let startLeft = e.clientX,
startTop = e.clientY;
// 拖动节点
const mousemoveFn = (moveEvent) => {
let endLeft = styleLeft + (moveEvent.clientX - startLeft),
endTop = styleTop + (moveEvent.clientY - startTop);
target.style.left = endLeft + 'px';
target.style.top = endTop + 'px';
this.changeNodeSort(target, endLeft, endTop);
}
document.addEventListener('mousemove', mousemoveFn);
// 释放拖动的节点
const mouseupFn = () => {
// 因为移动的时候要保证移动位置,不能对当前节点排序,所有鼠标释放,进行下排序
this.sortNodePosition([target]);
setTimeout(() => {target.removeAttribute('class')});
// 这步可以不做,为了和谷歌一样,把dom也进行排序
this.sortDom();
document.removeEventListener('mousemove', mousemoveFn);
document.removeEventListener('mouseup', mouseupFn);
}
document.addEventListener('mouseup', mouseupFn);
},
/**
* 计算出需要排序变化的节点,改变节点sort属性
*/
changeNodeSort(dragNode, x, y) {
// 元素移动:水平方向超过 nodeWidth 一半,算sort+1;垂直方向超过 nodeHeight 一半,算进入到下一排(sort + nodeNumber)
var newSort = Math.round(y / this.nodeHeight) * this.nodeNumber + Math.round(x / this.nodeWidth);
newSort = newSort > this.nodeList.length - 1 ? this.nodeList.length - 1 : newSort;
newSort = newSort < 0 ? 0 : newSort;
// 在移动过程中,当前节点sort没有改变,不需要重复计算排序。
if (newSort !== this.index) {
this.index = newSort;
var oldSort = dragNode.sort;
this.nodeList.forEach(node => {
// 往前移动,老位置 - 新位置(包含)之间的所有节点,排序都加1
if (newSort < oldSort) {
if (node.sort >= newSort && node.sort < oldSort) node.sort++;
}
// 往后移动,老位置 - 新位置(包含)之间的所有节点,排序都减1
else {
if (node.sort <= newSort && node.sort > oldSort) node.sort--;
}
})
// 给当前节点排序重新赋值,不要漏了这个
dragNode.sort = newSort
// 把除了当前节点的,所有节点都重新排序下位置
this.sortNodePosition(this.nodeList.filter(node => node!==dragNode));
}
},
/**
* 对DOM排序
*/
sortDom() {
let newSortWrap = document.createDocumentFragment();
this.nodeList = [...this.el.children].sort((a,b) => a.sort - b.sort);
this.nodeList.forEach(node => {
newSortWrap.appendChild(node);
});
this.el.appendChild(newSortWrap);
}
}
dragSort.init(document.querySelector('.drag-wrap'));
</script>
</body>
</html>
网友评论