js拖拽自动排序

2/16/2022 JavaScript

“ 在开发中可能你会在移动端看到标签拖拽自动排序的案例,接下来就详细的介绍关于PC端的例子,移动端和PC端相差不会太大。”

# 使用场景

在开发中可能你会在移动端看到标签拖拽自动排序的案例,下面我举一个动态例子可能你就能更加清楚明白是什么案例了,看下面的动态图:

# 重要思路

找到参考点:鼠标点击选中当前的元素时,当这个元素移动到另一个元素加上元素之间距离的和的一半时(在这里元素的长度加上元素之间的距离的和是125px,每一行是5个元素),那么就要使用一个重要的公式:Math.round(x/125)+Math.round(y/125)*5,x=left + e.pageX - startX,y=top + e.pageY - startY(startX=e.pageX,startY=e.pageY,在这里它们是鼠标点击时的事件),接下来我来解释一下该公式的意思:Math.round(x/125)表示你移动几列,当超过一半时四舍五入为1,Math.round(y/125)表示你移动的行数,当超过一半时四舍五入为1,并且它需要乘以每一行的个数,即列数。

# 小例子

可能你还是不是很清楚,我再来举一个简单的小例子:假设总共有两行,每一行有5个元素,即:

1 2 3 4 5

6 7 8 9 10

假设将2移动到6的位置,在这里2移动到6的时候,它的x轴的移动距离等价于2移动到1的x轴距离,这个时候x=left+e.pageX-startX=0,这个时候首先2的x轴变为0,同理2的y轴是四舍五入之后变为+1,然后再乘以一行的个数5,得出值为:0+1*5=5。

# 移动的条件

移动的下标不等于初始化的下标。当从小移动到大时,在这个范围内的序列都要-1;当从大移动到小时,在这个范围内的序列都要+1。

# 计算下标的临界点

如果移动计算出来的下标当前最大值时,将它置为最大值,如果移动计算出来的下标当前小于0时,将它置为0。

# 代码实现

<!DOCTYPE html>
<html lang="zh">
    <head>
        <meta charset="UTF-8">
        <title>js拖拽自动排序</title>
        <style>
            * {
                padding: 0;
                margin: 0;
            }

            html,
            body {
                width: 100%;
                height: 100%;
                user-select: none;
            }

            h1 {
                text-align: center;
            }

            .wrap {
                position: relative;
                width: 600px;
                height: 400px;
                margin: 100px auto;
                border: solid 1px black;
            }

            .wrap div {
                position: absolute;
                z-index: 1;
                width: 100px;
                height: 100px;
                background: rgb(243, 243, 243);
                /* 保证剩下的元素在移动时可以有一个过渡效果 */
                transition: all 0.5s;
            }

            .wrap p {
                background: gray;
                color: #fff;
                padding-left: 5px;
            }
        </style>
    </head>

    <body>
        <h1>js拖拽自动排序</h1>
        <div class="wrap" id="elWrap">
            <div><p>1</p></div>
            <div><p>2</p></div>
            <div><p>3</p></div>
            <div><p>4</p></div>
            <div><p>5</p></div>
            <div><p>6</p></div>
            <div><p>7</p></div>
            <div><p>8</p></div>
            <div><p>9</p></div>
            <div><p>10</p></div>
            <div><p>11</p></div>
            <div><p>12</p></div>
            <div><p>13</p></div>
            <div><p>14</p></div>
            <div><p>15</p></div>
        </div>

        <script>
            let index = 0  // 初始化下标
            let elArr = document.getElementById('elWrap').children
            let elList = []  // 存储每个元素的字段
            for (let i = 0; i < elArr.length; i++) {
            // el代表这个元素,sort代表元素排列的位置,index代表当前元素的index,它是不会改变的
            elList.push({ el: elArr[i], sort: i, index: i })
            // 给每个元素绑定事件
            elList[i].onClick = addEvent(elList[i])
            }
            // 初始化给div元素进行排列
            moveItem(elList, 125)

            function addEvent(item) {
                item.el.addEventListener('mousedown', (e) => {
                    item.el.style.zIndex = 2
                    item.el.style.transition = 'all 0s' // 鼠标在按下时过渡为0,避免在移动时出现过渡效果
                    let startX = e.pageX  // 鼠标相对于浏览器有效区域x坐标(随滚动条滚动而改变)
                    let startY = e.pageY
                    let left = item.el.offsetLeft  // 元素外边距离(绝对定位参照)最近定位父元素内壁的距离
                    let top = item.el.offsetTop

                    let moveFun = (e) => {
                        let X = left + e.pageX - startX
                        let Y = top + e.pageY - startY
                        item.el.style.left = X + 'px'
                        item.el.style.top = Y + 'px'
                        // 其他元素随着点击元素的移动而移动
                        reRange(item, X, Y, 125)  // 在这里125表示元素的自身长度以及元素之间距离的和
                    }
                    document.addEventListener('mousemove', moveFun)
                    document.addEventListener('mouseup', () => {
                        document.removeEventListener('mousemove', moveFun)  // 移除鼠标移动事件
                        item.el.style.zIndex = 1
                        item.el.style.transition = 'all 0.5s'  // 保证鼠标在松开时出现过渡效果
                        // 鼠标松开之后需重新的一个更新页面效果
                        moveItem(elList, 125)
                    })
                })
            }

            function reRange(item, x, y, width) { // width表示元素的自身长度以及元素之间距离的和
                /* 
                * 判断条件:移动到另一个元素一半时就可以替换这个元素
                * 行:四舍五入为1,列:四舍五入为1 * 一行的个数
                * 举一个例子:1 2 3 4 5
                *            6 7 8 9 10
                * 假设将2移动到6的位置,这个时候首先2的x轴变为0,2的y轴是+1
                * 然后再乘以一行的个数5,得出的moveIndex为:0+1*5=5
                */
                let moveIndex = Math.round(x / width) + Math.round(y / width) * 5  // 鼠标点击选中当前的元素
                moveIndex = moveIndex < 0 ? 0 : moveIndex  // 当移动下标小于0时,将它置为0
                moveIndex = moveIndex > elList.length - 1 ? elList.length - 1 : moveIndex  // 当移动下标大于最大值,将它置为最大值
                if (moveIndex !== index) { // 移动下标不等于初始化下标,可以发生移动
                    index = moveIndex
                    let currentSort = item.sort  // 当前元素排列的位置
                    for (let i = 0; i < elList.length; i++) {
                    if (currentSort < moveIndex) {  // 从小到大,所有排序-1
                        if (elList[i].sort >= currentSort && elList[i].sort <= moveIndex) {
                        elList[i].sort -= 1
                        }
                    } else if (currentSort > moveIndex) {  // 从大到小,所有排序+1
                        if (elList[i].sort <= currentSort && elList[i].sort >= moveIndex) {
                        elList[i].sort += 1
                        }
                    }
                    }
                    // 将点击的元素的sort置为moveIndex
                    elList[item.index].sort = moveIndex
                    // 在鼠标移动的时候也需要重新的更新页面效果
                    moveItem(elList, 125)
                }
            }

            // 排列 
            function moveItem(elList, width) {
                for (let i = 0; i < elList.length; i++) {
                    elList[i].el.style.left = elList[i].sort % 5 * width + 'px'  // 列如何分布
                    elList[i].el.style.top = parseInt(elList[i].sort / 5) * width + 'px'  // 行如何分布
                }
            }
        </script>
    </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152