h5拖拽功能 发表于 2021-07-14 | 直接上html例子,分为任意悬停和磁吸悬停 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183<!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>