fix: some timeline issues
This commit is contained in:
@ -21,11 +21,9 @@ export function TimelineClip({
|
|||||||
onClipClick,
|
onClipClick,
|
||||||
}: TimelineClipProps) {
|
}: TimelineClipProps) {
|
||||||
const { mediaItems } = useMediaStore();
|
const { mediaItems } = useMediaStore();
|
||||||
const { updateClipTrim, addClipToTrack, removeClipFromTrack } =
|
const { updateClipTrim, addClipToTrack, removeClipFromTrack, dragState } =
|
||||||
useTimelineStore();
|
useTimelineStore();
|
||||||
const { currentTime } = usePlaybackStore();
|
const { currentTime } = usePlaybackStore();
|
||||||
const { draggedClipId, getDraggedClipPosition } =
|
|
||||||
useDragClip(zoomLevel);
|
|
||||||
|
|
||||||
const [resizing, setResizing] = useState<ResizeState | null>(null);
|
const [resizing, setResizing] = useState<ResizeState | null>(null);
|
||||||
const [clipMenuOpen, setClipMenuOpen] = useState(false);
|
const [clipMenuOpen, setClipMenuOpen] = useState(false);
|
||||||
@ -34,12 +32,13 @@ export function TimelineClip({
|
|||||||
const clipWidth = Math.max(80, effectiveDuration * 50 * zoomLevel);
|
const clipWidth = Math.max(80, effectiveDuration * 50 * zoomLevel);
|
||||||
|
|
||||||
// Use real-time position during drag, otherwise use stored position
|
// Use real-time position during drag, otherwise use stored position
|
||||||
const dragPosition = getDraggedClipPosition(clip.id);
|
const isBeingDragged = dragState.clipId === clip.id;
|
||||||
const clipStartTime = dragPosition !== null ? dragPosition : clip.startTime;
|
const clipStartTime =
|
||||||
|
isBeingDragged && dragState.isDragging
|
||||||
|
? dragState.currentTime
|
||||||
|
: clip.startTime;
|
||||||
const clipLeft = clipStartTime * 50 * zoomLevel;
|
const clipLeft = clipStartTime * 50 * zoomLevel;
|
||||||
|
|
||||||
const isBeingDragged = draggedClipId === clip.id;
|
|
||||||
|
|
||||||
const getTrackColor = (type: string) => {
|
const getTrackColor = (type: string) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "video":
|
case "video":
|
||||||
@ -210,7 +209,7 @@ export function TimelineClip({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`timeline-clip absolute h-full border ${getTrackColor(track.type)} flex items-center py-3 min-w-[80px] overflow-hidden group hover:shadow-lg ${isSelected ? "ring-2 ring-blue-500 z-10" : ""} ${isBeingDragged ? "shadow-lg z-20" : ""} ${isBeingDragged ? "cursor-grabbing" : "cursor-grab"}`}
|
className={`timeline-clip absolute h-full border ${getTrackColor(track.type)} flex items-center py-3 min-w-[80px] overflow-hidden group hover:shadow-lg ${isSelected ? "ring-2 ring-blue-500 z-10" : ""} ${isBeingDragged ? "shadow-lg z-20" : ""}`}
|
||||||
style={{ width: `${clipWidth}px`, left: `${clipLeft}px` }}
|
style={{ width: `${clipWidth}px`, left: `${clipLeft}px` }}
|
||||||
onMouseDown={(e) => onClipMouseDown(e, clip)}
|
onMouseDown={(e) => onClipMouseDown(e, clip)}
|
||||||
onClick={(e) => onClipClick(e, clip)}
|
onClick={(e) => onClipClick(e, clip)}
|
||||||
|
@ -60,6 +60,14 @@ export function Timeline() {
|
|||||||
updateClipTrim,
|
updateClipTrim,
|
||||||
undo,
|
undo,
|
||||||
redo,
|
redo,
|
||||||
|
moveClipToTrack,
|
||||||
|
updateClipStartTime,
|
||||||
|
selectClip,
|
||||||
|
deselectClip,
|
||||||
|
dragState,
|
||||||
|
startDrag: startDragAction,
|
||||||
|
updateDragTime,
|
||||||
|
endDrag: endDragAction,
|
||||||
} = useTimelineStore();
|
} = useTimelineStore();
|
||||||
const { mediaItems, addMediaItem } = useMediaStore();
|
const { mediaItems, addMediaItem } = useMediaStore();
|
||||||
const {
|
const {
|
||||||
@ -941,7 +949,7 @@ export function Timeline() {
|
|||||||
{/* Playhead for tracks area (scrubbable) */}
|
{/* Playhead for tracks area (scrubbable) */}
|
||||||
{tracks.length > 0 && (
|
{tracks.length > 0 && (
|
||||||
<div
|
<div
|
||||||
className="absolute top-0 w-0.5 bg-red-500 pointer-events-auto z-20 cursor-ew-resize"
|
className="absolute top-0 w-0.5 bg-red-500 pointer-events-auto z-20"
|
||||||
style={{
|
style={{
|
||||||
left: `${playheadPosition * 50 * zoomLevel}px`,
|
left: `${playheadPosition * 50 * zoomLevel}px`,
|
||||||
height: `${tracks.length * 60}px`,
|
height: `${tracks.length * 60}px`,
|
||||||
@ -1143,11 +1151,13 @@ function TimelineTrackContent({
|
|||||||
selectedClips,
|
selectedClips,
|
||||||
selectClip,
|
selectClip,
|
||||||
deselectClip,
|
deselectClip,
|
||||||
|
dragState,
|
||||||
|
startDrag: startDragAction,
|
||||||
|
updateDragTime,
|
||||||
|
endDrag: endDragAction,
|
||||||
} = useTimelineStore();
|
} = useTimelineStore();
|
||||||
|
|
||||||
// Mouse-based drag hook
|
const timelineRef = useRef<HTMLDivElement>(null);
|
||||||
const { isDragging, startDrag, endDrag, timelineRef } =
|
|
||||||
useDragClip(zoomLevel);
|
|
||||||
const [isDropping, setIsDropping] = useState(false);
|
const [isDropping, setIsDropping] = useState(false);
|
||||||
const [dropPosition, setDropPosition] = useState<number | null>(null);
|
const [dropPosition, setDropPosition] = useState<number | null>(null);
|
||||||
const [wouldOverlap, setWouldOverlap] = useState(false);
|
const [wouldOverlap, setWouldOverlap] = useState(false);
|
||||||
@ -1155,6 +1165,92 @@ function TimelineTrackContent({
|
|||||||
|
|
||||||
const [justFinishedDrag, setJustFinishedDrag] = useState(false);
|
const [justFinishedDrag, setJustFinishedDrag] = useState(false);
|
||||||
|
|
||||||
|
// Set up mouse event listeners for drag
|
||||||
|
useEffect(() => {
|
||||||
|
if (!dragState.isDragging) return;
|
||||||
|
|
||||||
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
|
if (!timelineRef.current) return;
|
||||||
|
|
||||||
|
const timelineRect = timelineRef.current.getBoundingClientRect();
|
||||||
|
const mouseX = e.clientX - timelineRect.left;
|
||||||
|
const mouseTime = Math.max(0, mouseX / (50 * zoomLevel));
|
||||||
|
const adjustedTime = Math.max(0, mouseTime - dragState.clickOffsetTime);
|
||||||
|
const snappedTime = Math.round(adjustedTime * 10) / 10;
|
||||||
|
|
||||||
|
updateDragTime(snappedTime);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
if (!dragState.clipId || !dragState.trackId) return;
|
||||||
|
|
||||||
|
const finalTime = dragState.currentTime;
|
||||||
|
|
||||||
|
// Check for overlaps and update position
|
||||||
|
const sourceTrack = tracks.find((t) => t.id === dragState.trackId);
|
||||||
|
const movingClip = sourceTrack?.clips.find(
|
||||||
|
(c) => c.id === dragState.clipId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (movingClip) {
|
||||||
|
const movingClipDuration =
|
||||||
|
movingClip.duration - movingClip.trimStart - movingClip.trimEnd;
|
||||||
|
const movingClipEnd = finalTime + movingClipDuration;
|
||||||
|
|
||||||
|
const targetTrack = tracks.find((t) => t.id === track.id);
|
||||||
|
const hasOverlap = targetTrack?.clips.some((existingClip) => {
|
||||||
|
if (
|
||||||
|
dragState.trackId === track.id &&
|
||||||
|
existingClip.id === dragState.clipId
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const existingStart = existingClip.startTime;
|
||||||
|
const existingEnd =
|
||||||
|
existingClip.startTime +
|
||||||
|
(existingClip.duration -
|
||||||
|
existingClip.trimStart -
|
||||||
|
existingClip.trimEnd);
|
||||||
|
return finalTime < existingEnd && movingClipEnd > existingStart;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasOverlap) {
|
||||||
|
if (dragState.trackId === track.id) {
|
||||||
|
updateClipStartTime(track.id, dragState.clipId, finalTime);
|
||||||
|
} else {
|
||||||
|
moveClipToTrack(dragState.trackId, track.id, dragState.clipId);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
updateClipStartTime(track.id, dragState.clipId!, finalTime);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endDragAction();
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("mousemove", handleMouseMove);
|
||||||
|
document.addEventListener("mouseup", handleMouseUp);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousemove", handleMouseMove);
|
||||||
|
document.removeEventListener("mouseup", handleMouseUp);
|
||||||
|
};
|
||||||
|
}, [
|
||||||
|
dragState.isDragging,
|
||||||
|
dragState.clickOffsetTime,
|
||||||
|
dragState.clipId,
|
||||||
|
dragState.trackId,
|
||||||
|
dragState.currentTime,
|
||||||
|
zoomLevel,
|
||||||
|
tracks,
|
||||||
|
track.id,
|
||||||
|
updateDragTime,
|
||||||
|
updateClipStartTime,
|
||||||
|
moveClipToTrack,
|
||||||
|
endDragAction,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleClipMouseDown = (e: React.MouseEvent, clip: TypeTimelineClip) => {
|
const handleClipMouseDown = (e: React.MouseEvent, clip: TypeTimelineClip) => {
|
||||||
// Handle selection first
|
// Handle selection first
|
||||||
if (!justFinishedDrag) {
|
if (!justFinishedDrag) {
|
||||||
@ -1178,23 +1274,39 @@ function TimelineTrackContent({
|
|||||||
const clickOffsetX = e.clientX - clipRect.left;
|
const clickOffsetX = e.clientX - clipRect.left;
|
||||||
const clickOffsetTime = clickOffsetX / (50 * zoomLevel);
|
const clickOffsetTime = clickOffsetX / (50 * zoomLevel);
|
||||||
|
|
||||||
startDrag(e, clip.id, track.id, clip.startTime, clickOffsetTime);
|
startDragAction(
|
||||||
|
clip.id,
|
||||||
|
track.id,
|
||||||
|
e.clientX,
|
||||||
|
clip.startTime,
|
||||||
|
clickOffsetTime
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClipClick = (e: React.MouseEvent, clip: TypeTimelineClip) => {
|
const handleClipClick = (e: React.MouseEvent, clip: TypeTimelineClip) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"handleClipClick called, contextMenu:",
|
||||||
|
JSON.stringify(contextMenu)
|
||||||
|
);
|
||||||
|
console.log("Boolean check:", !!contextMenu, "Type:", typeof contextMenu);
|
||||||
|
|
||||||
// Don't handle click if we just finished dragging
|
// Don't handle click if we just finished dragging
|
||||||
if (justFinishedDrag) {
|
if (justFinishedDrag) {
|
||||||
|
console.log("Skipping because justFinishedDrag");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close context menu if it's open
|
// Close context menu if it's open
|
||||||
if (contextMenu) {
|
if (contextMenu) {
|
||||||
|
console.log("Closing context menu");
|
||||||
setContextMenu(null);
|
setContextMenu(null);
|
||||||
return; // Don't handle selection when closing context menu
|
return; // Don't handle selection when closing context menu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Proceeding to selection logic");
|
||||||
|
|
||||||
// Only handle deselection here (selection is handled in mouseDown)
|
// Only handle deselection here (selection is handled in mouseDown)
|
||||||
const isSelected = selectedClips.some(
|
const isSelected = selectedClips.some(
|
||||||
(c) => c.trackId === track.id && c.clipId === clip.id
|
(c) => c.trackId === track.id && c.clipId === clip.id
|
||||||
@ -1220,13 +1332,14 @@ function TimelineTrackContent({
|
|||||||
|
|
||||||
// Reset drag flag when drag ends
|
// Reset drag flag when drag ends
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isDragging && justFinishedDrag) {
|
if (!dragState.isDragging && justFinishedDrag) {
|
||||||
const timer = setTimeout(() => setJustFinishedDrag(false), 50);
|
const timer = setTimeout(() => setJustFinishedDrag(false), 50);
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
} else if (isDragging && !justFinishedDrag) {
|
} else if (!dragState.isDragging && dragState.clipId && !justFinishedDrag) {
|
||||||
|
// Only set justFinishedDrag when a drag actually ends (not when it starts)
|
||||||
setJustFinishedDrag(true);
|
setJustFinishedDrag(true);
|
||||||
}
|
}
|
||||||
}, [isDragging, justFinishedDrag]);
|
}, [dragState.isDragging, justFinishedDrag, dragState.clipId]);
|
||||||
|
|
||||||
const handleTrackDragOver = (e: React.DragEvent) => {
|
const handleTrackDragOver = (e: React.DragEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -1576,11 +1689,6 @@ function TimelineTrackContent({
|
|||||||
onDragEnter={handleTrackDragEnter}
|
onDragEnter={handleTrackDragEnter}
|
||||||
onDragLeave={handleTrackDragLeave}
|
onDragLeave={handleTrackDragLeave}
|
||||||
onDrop={handleTrackDrop}
|
onDrop={handleTrackDrop}
|
||||||
onMouseUp={(e) => {
|
|
||||||
if (isDragging) {
|
|
||||||
endDrag(track.id);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref={timelineRef}
|
ref={timelineRef}
|
||||||
|
@ -25,6 +25,10 @@ export function useDragClip(zoomLevel: number) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const timelineRef = useRef<HTMLDivElement>(null);
|
const timelineRef = useRef<HTMLDivElement>(null);
|
||||||
|
const dragStateRef = useRef(dragState);
|
||||||
|
|
||||||
|
// Keep ref in sync with state
|
||||||
|
dragStateRef.current = dragState;
|
||||||
|
|
||||||
const startDrag = useCallback(
|
const startDrag = useCallback(
|
||||||
(
|
(
|
||||||
@ -52,7 +56,9 @@ export function useDragClip(zoomLevel: number) {
|
|||||||
|
|
||||||
const updateDrag = useCallback(
|
const updateDrag = useCallback(
|
||||||
(e: MouseEvent) => {
|
(e: MouseEvent) => {
|
||||||
if (!dragState.isDragging || !timelineRef.current) return;
|
if (!dragState.isDragging || !timelineRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const timelineRect = timelineRef.current.getBoundingClientRect();
|
const timelineRect = timelineRef.current.getBoundingClientRect();
|
||||||
const mouseX = e.clientX - timelineRect.left;
|
const mouseX = e.clientX - timelineRect.left;
|
||||||
@ -172,12 +178,17 @@ export function useDragClip(zoomLevel: number) {
|
|||||||
|
|
||||||
const getDraggedClipPosition = useCallback(
|
const getDraggedClipPosition = useCallback(
|
||||||
(clipId: string) => {
|
(clipId: string) => {
|
||||||
if (dragState.isDragging && dragState.clipId === clipId) {
|
// Use ref to get current state, not stale closure
|
||||||
return dragState.currentTime;
|
const currentDragState = dragStateRef.current;
|
||||||
|
const isMatch =
|
||||||
|
currentDragState.isDragging && currentDragState.clipId === clipId;
|
||||||
|
|
||||||
|
if (isMatch) {
|
||||||
|
return currentDragState.currentTime;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
[dragState]
|
[] // No dependencies needed since we use ref
|
||||||
);
|
);
|
||||||
|
|
||||||
const isValidDropTarget = useCallback(
|
const isValidDropTarget = useCallback(
|
||||||
@ -199,6 +210,8 @@ export function useDragClip(zoomLevel: number) {
|
|||||||
// State
|
// State
|
||||||
isDragging: dragState.isDragging,
|
isDragging: dragState.isDragging,
|
||||||
draggedClipId: dragState.clipId,
|
draggedClipId: dragState.clipId,
|
||||||
|
currentDragTime: dragState.currentTime,
|
||||||
|
clickOffsetTime: dragState.clickOffsetTime,
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
startDrag,
|
startDrag,
|
||||||
|
@ -30,6 +30,27 @@ interface TimelineStore {
|
|||||||
clearSelectedClips: () => void;
|
clearSelectedClips: () => void;
|
||||||
setSelectedClips: (clips: { trackId: string; clipId: string }[]) => void;
|
setSelectedClips: (clips: { trackId: string; clipId: string }[]) => void;
|
||||||
|
|
||||||
|
// Drag state
|
||||||
|
dragState: {
|
||||||
|
isDragging: boolean;
|
||||||
|
clipId: string | null;
|
||||||
|
trackId: string | null;
|
||||||
|
startMouseX: number;
|
||||||
|
startClipTime: number;
|
||||||
|
clickOffsetTime: number;
|
||||||
|
currentTime: number;
|
||||||
|
};
|
||||||
|
setDragState: (dragState: Partial<TimelineStore["dragState"]>) => void;
|
||||||
|
startDrag: (
|
||||||
|
clipId: string,
|
||||||
|
trackId: string,
|
||||||
|
startMouseX: number,
|
||||||
|
startClipTime: number,
|
||||||
|
clickOffsetTime: number
|
||||||
|
) => void;
|
||||||
|
updateDragTime: (currentTime: number) => void;
|
||||||
|
endDrag: () => void;
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
addTrack: (type: "video" | "audio" | "effects") => string;
|
addTrack: (type: "video" | "audio" | "effects") => string;
|
||||||
removeTrack: (trackId: string) => void;
|
removeTrack: (trackId: string) => void;
|
||||||
@ -73,7 +94,7 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
|
|||||||
// Deep copy tracks
|
// Deep copy tracks
|
||||||
set({
|
set({
|
||||||
history: [...history, JSON.parse(JSON.stringify(tracks))],
|
history: [...history, JSON.parse(JSON.stringify(tracks))],
|
||||||
redoStack: [] // Clear redo stack when new action is performed
|
redoStack: [], // Clear redo stack when new action is performed
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -84,7 +105,7 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
|
|||||||
set({
|
set({
|
||||||
tracks: prev,
|
tracks: prev,
|
||||||
history: history.slice(0, -1),
|
history: history.slice(0, -1),
|
||||||
redoStack: [...redoStack, JSON.parse(JSON.stringify(tracks))] // Add current state to redo stack
|
redoStack: [...redoStack, JSON.parse(JSON.stringify(tracks))], // Add current state to redo stack
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -96,7 +117,11 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
|
|||||||
if (multi) {
|
if (multi) {
|
||||||
// Toggle selection
|
// Toggle selection
|
||||||
return exists
|
return exists
|
||||||
? { selectedClips: state.selectedClips.filter((c) => !(c.trackId === trackId && c.clipId === clipId)) }
|
? {
|
||||||
|
selectedClips: state.selectedClips.filter(
|
||||||
|
(c) => !(c.trackId === trackId && c.clipId === clipId)
|
||||||
|
),
|
||||||
|
}
|
||||||
: { selectedClips: [...state.selectedClips, { trackId, clipId }] };
|
: { selectedClips: [...state.selectedClips, { trackId, clipId }] };
|
||||||
} else {
|
} else {
|
||||||
return { selectedClips: [{ trackId, clipId }] };
|
return { selectedClips: [{ trackId, clipId }] };
|
||||||
@ -105,7 +130,9 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
|
|||||||
},
|
},
|
||||||
deselectClip: (trackId, clipId) => {
|
deselectClip: (trackId, clipId) => {
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
selectedClips: state.selectedClips.filter((c) => !(c.trackId === trackId && c.clipId === clipId)),
|
selectedClips: state.selectedClips.filter(
|
||||||
|
(c) => !(c.trackId === trackId && c.clipId === clipId)
|
||||||
|
),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
clearSelectedClips: () => {
|
clearSelectedClips: () => {
|
||||||
@ -161,7 +188,10 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
|
|||||||
tracks: state.tracks
|
tracks: state.tracks
|
||||||
.map((track) =>
|
.map((track) =>
|
||||||
track.id === trackId
|
track.id === trackId
|
||||||
? { ...track, clips: track.clips.filter((clip) => clip.id !== clipId) }
|
? {
|
||||||
|
...track,
|
||||||
|
clips: track.clips.filter((clip) => clip.id !== clipId),
|
||||||
|
}
|
||||||
: track
|
: track
|
||||||
)
|
)
|
||||||
// Remove track if it becomes empty
|
// Remove track if it becomes empty
|
||||||
@ -261,4 +291,56 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
|
|||||||
const next = redoStack[redoStack.length - 1];
|
const next = redoStack[redoStack.length - 1];
|
||||||
set({ tracks: next, redoStack: redoStack.slice(0, -1) });
|
set({ tracks: next, redoStack: redoStack.slice(0, -1) });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
dragState: {
|
||||||
|
isDragging: false,
|
||||||
|
clipId: null,
|
||||||
|
trackId: null,
|
||||||
|
startMouseX: 0,
|
||||||
|
startClipTime: 0,
|
||||||
|
clickOffsetTime: 0,
|
||||||
|
currentTime: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
setDragState: (dragState) =>
|
||||||
|
set((state) => ({
|
||||||
|
dragState: { ...state.dragState, ...dragState },
|
||||||
|
})),
|
||||||
|
|
||||||
|
startDrag: (clipId, trackId, startMouseX, startClipTime, clickOffsetTime) => {
|
||||||
|
set({
|
||||||
|
dragState: {
|
||||||
|
isDragging: true,
|
||||||
|
clipId,
|
||||||
|
trackId,
|
||||||
|
startMouseX,
|
||||||
|
startClipTime,
|
||||||
|
clickOffsetTime,
|
||||||
|
currentTime: startClipTime,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateDragTime: (currentTime) => {
|
||||||
|
set((state) => ({
|
||||||
|
dragState: {
|
||||||
|
...state.dragState,
|
||||||
|
currentTime,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
endDrag: () => {
|
||||||
|
set({
|
||||||
|
dragState: {
|
||||||
|
isDragging: false,
|
||||||
|
clipId: null,
|
||||||
|
trackId: null,
|
||||||
|
startMouseX: 0,
|
||||||
|
startClipTime: 0,
|
||||||
|
clickOffsetTime: 0,
|
||||||
|
currentTime: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
Reference in New Issue
Block a user