Files
kiss-translator/src/views/Action/Draggable.js
2023-08-17 13:27:22 +08:00

208 lines
4.5 KiB
JavaScript

import { useCallback, useEffect, useMemo, useState } from "react";
import { limitNumber } from "../../libs/utils";
import { isMobile } from "../../libs/mobile";
import { setFab } from "../../libs";
const getEdgePosition = (
{ x: left, y: top, edge },
windowWidth,
windowHeight,
width,
height
) => {
const right = windowWidth - left - width;
const bottom = windowHeight - top - height;
const min = Math.min(left, top, right, bottom);
switch (min) {
case right:
edge = "right";
left = windowWidth - width;
break;
case left:
edge = "left";
left = 0;
break;
case bottom:
edge = "bottom";
top = windowHeight - height;
break;
default:
edge = "top";
top = 0;
}
left = limitNumber(left, 0, windowWidth - width);
top = limitNumber(top, 0, windowHeight - height);
return { x: left, y: top, edge, hide: false };
};
const getHidePosition = (
{ x: left, y: top, edge },
windowWidth,
windowHeight,
width,
height
) => {
switch (edge) {
case "right":
left = windowWidth - width / 2;
break;
case "left":
left = -width / 2;
break;
case "bottom":
top = windowHeight - height / 2;
break;
default:
top = -height / 2;
}
return { x: left, y: top, edge, hide: true };
};
export default function Draggable({
windowSize,
width,
height,
left,
top,
show,
snapEdge,
onStart,
onMove,
handler,
children,
}) {
const [origin, setOrigin] = useState({
x: left,
y: top,
px: left,
py: top,
});
const [position, setPosition] = useState({
x: left,
y: top,
edge: null,
hide: false,
});
const [edgeTimer, setEdgeTimer] = useState(null);
const goEdge = useCallback((w, h, width, height) => {
setPosition((pre) => getEdgePosition(pre, w, h, width, height));
setEdgeTimer(
setTimeout(() => {
setPosition((pre) => getHidePosition(pre, w, h, width, height));
}, 1500)
);
}, []);
const handlePointerDown = (e) => {
!isMobile && e.target.setPointerCapture(e.pointerId);
onStart && onStart();
edgeTimer && clearTimeout(edgeTimer);
const { clientX, clientY } = isMobile ? e.targetTouches[0] : e;
setOrigin({
x: position.x,
y: position.y,
px: clientX,
py: clientY,
});
};
const handlePointerMove = (e) => {
onMove && onMove();
const { clientX, clientY } = isMobile ? e.targetTouches[0] : e;
if (origin) {
const dx = clientX - origin.px;
const dy = clientY - origin.py;
let x = origin.x + dx;
let y = origin.y + dy;
const { w, h } = windowSize;
x = limitNumber(x, 0, w - width);
y = limitNumber(y, 0, h - height);
setPosition({ x, y, edge: null, hide: false });
}
};
const handlePointerUp = (e) => {
e.stopPropagation();
setOrigin(null);
if (!snapEdge) {
return;
}
goEdge(windowSize.w, windowSize.h, width, height);
};
const handleClick = (e) => {
e.stopPropagation();
};
const handleMouseEnter = (e) => {
e.stopPropagation();
if (snapEdge && position.hide) {
edgeTimer && clearTimeout(edgeTimer);
goEdge(windowSize.w, windowSize.h, width, height);
}
};
useEffect(() => {
setOrigin(null);
if (!snapEdge) {
return;
}
goEdge(windowSize.w, windowSize.h, width, height);
}, [snapEdge, goEdge, windowSize.w, windowSize.h, width, height]);
useEffect(() => {
if (position.hide) {
setFab({
x: position.x,
y: position.y,
});
}
}, [position]);
const opacity = useMemo(() => {
if (snapEdge) {
return position.hide ? 0.1 : 1;
}
return origin ? 0.8 : 1;
}, [origin, snapEdge, position.hide]);
const touchProps = isMobile
? {
onTouchStart: handlePointerDown,
onTouchMove: handlePointerMove,
onTouchEnd: handlePointerUp,
}
: {
onPointerDown: handlePointerDown,
onPointerMove: handlePointerMove,
onPointerUp: handlePointerUp,
};
return (
<div
style={{
opacity,
position: "fixed",
left: position.x,
top: position.y,
zIndex: 2147483647,
display: show ? "block" : "none",
}}
onMouseEnter={handleMouseEnter}
onClick={handleClick}
>
<div
style={{
touchAction: "none",
}}
{...touchProps}
>
{handler}
</div>
<div>{children}</div>
</div>
);
}