h5拖拽功能

直接上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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}

.draggable {
position: fixed;
bottom: 20px;
right: 20px;
background-color: gold;
width: 40px;
height: 60px;
}
</style>
</head>

<body>
<div class="draggable"></div>
<script>
function isAndroid() {
var u = navigator.userAgent
return u.indexOf('Android') > -1 || u.indexOf('Adr') > -1
}

const raf = window.requestAnimationFrame;

/**
* 封装拖拽函数
* @param $ele 需要拖拽的元素
* @param isAnywhere 是否可以任意位置悬停,false的话则表现为松手就贴边
* @param adsorb { x = 20, y = 80 } 上下左右吸附的边距
* @return Function 返回移除监听器的函数
*/
function draggable($ele, isAnywhere = false, adsorb = { x: 20, y: 20 }) {
if (!$ele) {
throw new Error("必须是可拖拽元素");
}
// 开始时候的位置
let startX = 0;
let startY = 0;

// 移动过程中的 left 和 top,其实通过这俩参数,就能确定元素位置
let left = 0;
let top = 0;

// 屏幕的宽高
const cw = document.documentElement.clientWidth;
const ch = document.documentElement.clientHeight;

// 获取到元素自身的宽高
const { width, height } = $ele.getBoundingClientRect();

function touchStart(event) {
startX = event.targetTouches[0].pageX;
startY = event.targetTouches[0].pageY;

top = $ele.offsetTop;
left = $ele.offsetLeft;

event.preventDefault();
}

function touchMove(event) {
// 偏移距离
const offsetX = event.targetTouches[0].pageX - startX;
const offsetY = event.targetTouches[0].pageY - startY;

$ele.style.top = `${top + offsetY}px`;
$ele.style.left = `${left + offsetX}px`;
$ele.style.right = "auto";
$ele.style.bottom = "auto";

event.preventDefault();
}

function touchDone(event) {
const dx = event.changedTouches[0].pageX - startX;
const dy = event.changedTouches[0].pageY - startY;

const ty = top + dy;
const tx = left + dx;

$ele.style.top = `${ty}px`;
$ele.style.left = `${tx}px`;
$ele.style.right = "auto";
$ele.style.bottom = "auto";

const adsorb_safe_x = cw - width - adsorb.x;
const adsorb_safe_y = ch - height - adsorb.y;

raf(() => {
let nx = tx;
let ny = ty;

// 处理不同的悬停方式
if (isAnywhere) {
if (tx < adsorb.x) {
nx = adsorb.x;
} else if (tx > (cw - width - adsorb.x)) {
nx = adsorb_safe_x;
}

if (ty < adsorb.y) {
ny = adsorb.y;
} else if (ty > (ch - height - adsorb.y)) {
ny = adsorb_safe_y;
}
} else {
if (tx + width / 2 < cw / 2) {
nx = adsorb.x;
} else {
nx = adsorb_safe_x;
}

if (ty < adsorb.y) {
ny = adsorb.y;
} else if (ty > adsorb_safe_y) {
ny = adsorb_safe_y;
}
}

// 无需移动,并且认为进行了一次点击
// click兼容处理
if (Math.abs(dx) < 5 && Math.abs(dy) < 5) {
$ele.click();
return;
}

const MOVE_ANIM_INTER = isAndroid() ?
Math.abs(((250 * (tx - nx)) / 800) | 0) :
200;

$ele.style.webkitTransition = `left ${MOVE_ANIM_INTER}ms linear, top ${MOVE_ANIM_INTER}ms linear`;
$ele.style.transition = `left ${MOVE_ANIM_INTER}ms linear, top ${MOVE_ANIM_INTER}ms linear`;

const onAnimationDone = () => {
$ele.style.webkitTransition = $ele.style.transition = "none";

$ele.removeEventListener("webkitTransitionEnd", onAnimationDone, false);
$ele.removeEventListener("transitionend", onAnimationDone, false);
};

$ele.addEventListener("webkitTransitionEnd", onAnimationDone, false);
$ele.addEventListener("transitionend", onAnimationDone, false);

$ele.style.top = `${ny}px`;
$ele.style.left = `${nx}px`;
});
}

$ele.addEventListener("touchstart", touchStart, false);
$ele.addEventListener("touchmove", touchMove, false);
$ele.addEventListener("touchend", touchDone, true);
$ele.addEventListener("touchcancel", touchDone, true);

return function() {
$ele.removeEventListener("touchstart", touchStart, false);
$ele.removeEventListener("touchmove", touchMove, false);
$ele.removeEventListener("touchend", touchDone, true);
$ele.removeEventListener("touchcancel", touchDone, true);
}
}

window.onload = function() {
const dragDom = document.querySelector('.draggable');
// touch和click互不冲突,里面已经做了click的兼容处理
window.addEventListener('click', () => {
alert('click event');
})
draggable(dragDom, true);
}
</script>
</body>

</html>