在浏览器实现可拖拽的元素(以img为)同时添加了onTouchStart、onTouchMove、onTouchEnd和onClick事件。
实现如下:
const FloatingButton = ({
src,
alt,
onClick,
draggable = false
}) => {
const [position, setPosition] = useState({});
const ref = useRef();
const startPoint = useRef();
const touchStartPoint = useRef();
const touchMovePoint = useRef();
const { width: screenWidth, height: screenHeight } = window.screen;
const calculatePosition = (pointOffset, currentSize, fullSize) => {
if (pointOffset < 0) {
return 0;
} else if (pointOffset + currentSize > fullSize) {
return fullSize - currentSize;
}
return pointOffset;
};
const left = () => {
const pointOffsetX = startPoint.current.x + (touchMovePoint.current.clientX - touchStartPoint.current.clientX);
return calculatePosition(pointOffsetX, startPoint.current.width, screenWidth);
};
const top = () => {
const pointOffsetY = startPoint.current.y + (touchMovePoint.current.clientY - touchStartPoint.current.clientY);
return calculatePosition(pointOffsetY, startPoint.current.height, screenHeight);
};
const handleClick = () => {
onClick();
};
const handleTouchStart = (e) => {
e.stopPropagation();
if (draggable) {
touchStartPoint.current = e.targetTouches[0];
startPoint.current = {
x: ref.current.getBoundingClientRect().left,
y: ref.current.getBoundingClientRect().top,
width: ref.current.width,
height: ref.current.height
};
}
};
const handleTouchMove = (e) => {
e.stopPropagation();
if (draggable) {
touchMovePoint.current = e.targetTouches[0];
setPosition({
left: left(),
top: top()
});
}
};
const handleTouchEnd = (e) => {
e.stopPropagation();
if (draggable) {
setPosition({
left: screenWidth / 2 > left() ? 0 : screenWidth - startPoint.current.width,
top: top()
});
}
};
return (
);
};
当在未拖动过的情况下,进行点击,会报错:
TypeError: Cannot read property 'clientX' of undefined
原因:
当同时监听touch和click事件时,触发顺序是 onTouchStart
=> onTouchEnd
=> onClick
所以touchMovePoint并未被赋值,导致报错。
虽然报错并不影响使用,但是还是想把它解决掉。
解决方案:
想要同时保留可以点击和可拖拽的功能,但是又要解决冲突,可以有2种解决方案。
//方案一
const FloatingButton = ({
src,
alt,
onClick,
draggable = false
}) => {
const [position, setPosition] = useState({});
const ref = useRef();
const startPoint = useRef();
const touchStartPoint = useRef();
const touchMovePoint = useRef();
const { width: screenWidth, height: screenHeight } = window.screen;
const calculatePosition = (pointOffset, currentSize, fullSize) => {
if (pointOffset < 0) {
return 0;
} else if (pointOffset + currentSize > fullSize) {
return fullSize - currentSize;
}
return pointOffset;
};
const left = () => {
const pointOffsetX = startPoint.current.x + (touchMovePoint.current.clientX - touchStartPoint.current.clientX);
return calculatePosition(pointOffsetX, startPoint.current.width, screenWidth);
};
const top = () => {
const pointOffsetY = startPoint.current.y + (touchMovePoint.current.clientY - touchStartPoint.current.clientY);
return calculatePosition(pointOffsetY, startPoint.current.height, screenHeight);
};
const handleClick = () => {
onClick();
};
const handleTouchStart = (e) => {
e.stopPropagation();
if (draggable) {
touchStartPoint.current = e.targetTouches[0];
startPoint.current = {
x: ref.current.getBoundingClientRect().left,
y: ref.current.getBoundingClientRect().top,
width: ref.current.width,
height: ref.current.height
};
}
};
const handleTouchMove = (e) => {
e.stopPropagation();
if (draggable) {
touchMovePoint.current = e.targetTouches[0];
setPosition({
left: left(),
top: top()
});
}
};
const handleTouchEnd = (e) => {
e.stopPropagation();
if (touchMovePoint && draggable) {
setPosition({
left: screenWidth / 2 > left() ? 0 : screenWidth - startPoint.current.width,
top: top()
});
touchMovePoint.current = null;
}
};
return (
);
};
//方案二
const FloatingButton = ({
src,
alt,
onClick,
draggable = false
}) => {
const [position, setPosition] = useState({});
const ref = useRef();
const startPoint = useRef();
const touchStartPoint = useRef();
const touchMovePoint = useRef();
const { width: screenWidth, height: screenHeight } = window.screen;
const calculatePosition = (pointOffset, currentSize, fullSize) => {
if (pointOffset < 0) {
return 0;
} else if (pointOffset + currentSize > fullSize) {
return fullSize - currentSize;
}
return pointOffset;
};
const left = () => {
const pointOffsetX = startPoint.current.x + (touchMovePoint.current.clientX - touchStartPoint.current.clientX);
return calculatePosition(pointOffsetX, startPoint.current.width, screenWidth);
};
const top = () => {
const pointOffsetY = startPoint.current.y + (touchMovePoint.current.clientY - touchStartPoint.current.clientY);
return calculatePosition(pointOffsetY, startPoint.current.height, screenHeight);
};
const handleClick = () => {
onClick();
};
const handleTouchStart = (e) => {
e.stopPropagation();
if (draggable) {
touchStartPoint.current = e.targetTouches[0];
startPoint.current = {
x: ref.current.getBoundingClientRect().left,
y: ref.current.getBoundingClientRect().top,
width: ref.current.width,
height: ref.current.height
};
}
};
const handleTouchMove = (e) => {
e.stopPropagation();
if (draggable) {
touchMovePoint.current = e.targetTouches[0];
setPosition({
left: left(),
top: top()
});
}
};
const handleTouchEnd = (e) => {
e.stopPropagation();
if (!touchMovePoint) {
handleClick();
} else if (draggable) {
setPosition({
left: screenWidth / 2 > left() ? 0 : screenWidth - startPoint.current.width,
top: top()
});
touchMovePoint.current = null;
}
};
return (
);
};