fix(3d): stabilize timeline dragging
This commit is contained in:
@@ -9,7 +9,11 @@ import {
|
||||
useState,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import Draggable from "react-draggable";
|
||||
import {
|
||||
DraggableCore,
|
||||
type DraggableData,
|
||||
type DraggableEvent,
|
||||
} from "react-draggable";
|
||||
import {
|
||||
FiCalendar,
|
||||
FiChevronDown,
|
||||
@@ -78,7 +82,15 @@ export function ThreeDimensionalTimeline({
|
||||
onSelectedDateChange,
|
||||
onCurrentTimeChange,
|
||||
}: ThreeDimensionalTimelineProps) {
|
||||
const timelineRef = useRef<HTMLDivElement>(null);
|
||||
const timelineRef = useRef<HTMLElement>(null);
|
||||
const dragShieldRef = useRef<HTMLDivElement>(null);
|
||||
const dragPositionRef = useRef({ x: 0, y: 0 });
|
||||
const dragStartRef = useRef<{
|
||||
pointerX: number;
|
||||
pointerY: number;
|
||||
originX: number;
|
||||
originY: number;
|
||||
} | null>(null);
|
||||
const [playing, setPlaying] = useState(false);
|
||||
const [playIntervalMs, setPlayIntervalMs] = useState(DEFAULT_PLAY_INTERVAL_MS);
|
||||
const [previewTime, setPreviewTime] = useState<number | null>(null);
|
||||
@@ -133,6 +145,58 @@ export function ThreeDimensionalTimeline({
|
||||
? Math.min(100, Math.max(0, (safeCurrentTime / durationMinutes) * 100))
|
||||
: 0;
|
||||
|
||||
const applyDragPosition = useCallback((x: number, y: number) => {
|
||||
dragPositionRef.current = { x, y };
|
||||
if (timelineRef.current) {
|
||||
timelineRef.current.style.transform = `translate3d(${x}px, ${y}px, 0)`;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleDragStart = useCallback(
|
||||
(_event: DraggableEvent, data: DraggableData) => {
|
||||
const { x: originX, y: originY } = dragPositionRef.current;
|
||||
dragStartRef.current = {
|
||||
pointerX: data.x,
|
||||
pointerY: data.y,
|
||||
originX,
|
||||
originY,
|
||||
};
|
||||
if (dragShieldRef.current) {
|
||||
dragShieldRef.current.style.display = "block";
|
||||
}
|
||||
if (timelineRef.current) {
|
||||
timelineRef.current.style.willChange = "transform";
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const handleDrag = useCallback(
|
||||
(_event: DraggableEvent, data: DraggableData) => {
|
||||
const start = dragStartRef.current;
|
||||
if (!start) return;
|
||||
applyDragPosition(
|
||||
start.originX + data.x - start.pointerX,
|
||||
start.originY + data.y - start.pointerY,
|
||||
);
|
||||
},
|
||||
[applyDragPosition],
|
||||
);
|
||||
|
||||
const handleDragStop = useCallback(
|
||||
(event: DraggableEvent, data: DraggableData) => {
|
||||
handleDrag(event, data);
|
||||
dragStartRef.current = null;
|
||||
if (dragShieldRef.current) {
|
||||
dragShieldRef.current.style.display = "";
|
||||
}
|
||||
if (timelineRef.current) {
|
||||
timelineRef.current.style.willChange = "";
|
||||
}
|
||||
},
|
||||
[handleDrag],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
@@ -140,17 +204,25 @@ export function ThreeDimensionalTimeline({
|
||||
sidePanelOpen ? "md:right-[416px]" : "md:right-4",
|
||||
)}
|
||||
>
|
||||
<Draggable
|
||||
<div
|
||||
ref={dragShieldRef}
|
||||
aria-hidden="true"
|
||||
className="pointer-events-auto fixed inset-0 z-0 hidden cursor-move"
|
||||
/>
|
||||
<DraggableCore
|
||||
nodeRef={timelineRef}
|
||||
handle=".timeline-drag-handle"
|
||||
cancel="button, input, select, [role='dialog']"
|
||||
onStart={handleDragStart}
|
||||
onDrag={handleDrag}
|
||||
onStop={handleDragStop}
|
||||
>
|
||||
<section
|
||||
ref={timelineRef}
|
||||
aria-label="三维场景时间轴"
|
||||
className={clsx(
|
||||
glassClass,
|
||||
"pointer-events-auto relative w-full max-w-[950px] rounded-2xl opacity-95 transition-opacity duration-200 hover:opacity-100",
|
||||
"pointer-events-auto relative z-10 w-full max-w-[950px] rounded-2xl opacity-95 transition-opacity duration-200 hover:opacity-100",
|
||||
)}
|
||||
>
|
||||
<div className="timeline-drag-handle relative flex h-7 cursor-move touch-none items-center justify-center rounded-t-2xl border-b border-white/40 bg-white/10">
|
||||
@@ -248,7 +320,7 @@ export function ThreeDimensionalTimeline({
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Draggable>
|
||||
</DraggableCore>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user