react实现自定义hooks(移动端拖拽)
实现思路
通过touchstart、touchmove、touchend组合,实现过程中需要注意以下几个问题
- touchmove、touchend事件需要绑定在window上,并且这两个事件需要包在touchstart中
- touchstart事件需要阻止冒泡和禁止默认事件,开始监听touchmove和touchmove。组件销毁前要记得移除事件监听
- touchend事件里要移除事件监听
- touchmove计算位置值,弄清楚event.clientX和element.offsetTop等关系
- 定位不要使用position,应该使用transform的translate
在线预览
核心代码
import { useEffect, useState, useRef } from "react";
import "./styles.css";
/***
* 请在移动端查看
*/
const docWidth = document.documentElement.clientWidth;
const docHeight = document.documentElement.clientHeight;
export default function useMobileDrag(props) {
const touchStartX = useRef(0);
const touchStartY = useRef(0);
const [x, setX] = useState(0);
const [y, setY] = useState(0);
const { targetDomRef } = props;
useEffect(() => {
if (!targetDomRef?.current) {
return;
}
const target = targetDomRef.current;
const { width, height } = target.getBoundingClientRect();
const touchmove = (e) => {
const { clientX, clientY } = e.targetTouches[0];
if (clientX - touchStartX.current >= docWidth - width) {
setX(docWidth - width);
} else if (clientX - touchStartX.current <= 0) {
setX(0);
} else {
setX(clientX - touchStartX.current);
}
if (clientY - touchStartY.current >= docHeight - height) {
setY(docHeight - height);
} else if (clientY - touchStartY.current <= 0) {
setY(0);
} else {
setY(clientY - touchStartY.current);
}
};
const touchend = () => {
window.removeEventListener("touchmove", touchmove);
window.removeEventListener("touchend", touchend);
};
const touchstart = (e) => {
e.stopPropagation();
e.preventDefault();
const { top, left } = target?.getBoundingClientRect();
const { clientX, clientY } = e.targetTouches[0];
touchStartX.current = clientX - left;
touchStartY.current = clientY - top;
window.addEventListener("touchmove", touchmove);
window.addEventListener("touchend", touchend);
};
target.addEventListener("touchstart", touchstart);
return () => {
target.removeEventListener("touchstart", touchstart);
};
}, [targetDomRef]);
return { x, y };
}