mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
fix: resize not matching with frames
This commit is contained in:
@@ -68,45 +68,51 @@ export function TimelineTrackContent({
|
|||||||
elementDuration: number,
|
elementDuration: number,
|
||||||
excludeElementId?: string
|
excludeElementId?: string
|
||||||
) => {
|
) => {
|
||||||
if (!snappingEnabled) {
|
// Always apply frame snapping first
|
||||||
// Use frame snapping if project has FPS, otherwise use decimal snapping
|
const projectStore = useProjectStore.getState();
|
||||||
const projectStore = useProjectStore.getState();
|
const projectFps = projectStore.activeProject?.fps || 30;
|
||||||
const projectFps = projectStore.activeProject?.fps || 30;
|
let finalTime = snapTimeToFrame(dropTime, projectFps);
|
||||||
return snapTimeToFrame(dropTime, projectFps);
|
|
||||||
|
// Additionally apply element snapping if enabled
|
||||||
|
if (snappingEnabled) {
|
||||||
|
// Try snapping both start and end edges for drops
|
||||||
|
const startSnapResult = snapElementEdge(
|
||||||
|
dropTime,
|
||||||
|
elementDuration,
|
||||||
|
tracks,
|
||||||
|
currentTime,
|
||||||
|
zoomLevel,
|
||||||
|
excludeElementId,
|
||||||
|
true // snap to start edge
|
||||||
|
);
|
||||||
|
|
||||||
|
const endSnapResult = snapElementEdge(
|
||||||
|
dropTime,
|
||||||
|
elementDuration,
|
||||||
|
tracks,
|
||||||
|
currentTime,
|
||||||
|
zoomLevel,
|
||||||
|
excludeElementId,
|
||||||
|
false // snap to end edge
|
||||||
|
);
|
||||||
|
|
||||||
|
// Choose the snap result with the smaller distance (closer snap)
|
||||||
|
let bestSnapResult = startSnapResult;
|
||||||
|
if (
|
||||||
|
endSnapResult.snapPoint &&
|
||||||
|
(!startSnapResult.snapPoint ||
|
||||||
|
endSnapResult.snapDistance < startSnapResult.snapDistance)
|
||||||
|
) {
|
||||||
|
bestSnapResult = endSnapResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only use element snapping if it found a snap point, otherwise keep frame-snapped time
|
||||||
|
if (bestSnapResult.snapPoint) {
|
||||||
|
finalTime = bestSnapResult.snappedTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try snapping both start and end edges for drops
|
return finalTime;
|
||||||
const startSnapResult = snapElementEdge(
|
|
||||||
dropTime,
|
|
||||||
elementDuration,
|
|
||||||
tracks,
|
|
||||||
currentTime,
|
|
||||||
zoomLevel,
|
|
||||||
excludeElementId,
|
|
||||||
true // snap to start edge
|
|
||||||
);
|
|
||||||
|
|
||||||
const endSnapResult = snapElementEdge(
|
|
||||||
dropTime,
|
|
||||||
elementDuration,
|
|
||||||
tracks,
|
|
||||||
currentTime,
|
|
||||||
zoomLevel,
|
|
||||||
excludeElementId,
|
|
||||||
false // snap to end edge
|
|
||||||
);
|
|
||||||
|
|
||||||
// Choose the snap result with the smaller distance (closer snap)
|
|
||||||
let bestSnapResult = startSnapResult;
|
|
||||||
if (
|
|
||||||
endSnapResult.snapPoint &&
|
|
||||||
(!startSnapResult.snapPoint ||
|
|
||||||
endSnapResult.snapDistance < startSnapResult.snapDistance)
|
|
||||||
) {
|
|
||||||
bestSnapResult = endSnapResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestSnapResult.snappedTime;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const timelineRef = useRef<HTMLDivElement>(null);
|
const timelineRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -148,9 +154,13 @@ export function TimelineTrackContent({
|
|||||||
);
|
);
|
||||||
const adjustedTime = Math.max(0, mouseTime - dragState.clickOffsetTime);
|
const adjustedTime = Math.max(0, mouseTime - dragState.clickOffsetTime);
|
||||||
|
|
||||||
// Apply snapping if enabled
|
// Always apply frame snapping first
|
||||||
let finalTime = adjustedTime;
|
const projectStore = useProjectStore.getState();
|
||||||
|
const projectFps = projectStore.activeProject?.fps || 30;
|
||||||
|
let finalTime = snapTimeToFrame(adjustedTime, projectFps);
|
||||||
let snapPoint = null;
|
let snapPoint = null;
|
||||||
|
|
||||||
|
// Additionally apply element snapping if enabled
|
||||||
if (snappingEnabled) {
|
if (snappingEnabled) {
|
||||||
// Find the element being dragged to get its duration
|
// Find the element being dragged to get its duration
|
||||||
let elementDuration = 5; // fallback duration
|
let elementDuration = 5; // fallback duration
|
||||||
@@ -196,18 +206,16 @@ export function TimelineTrackContent({
|
|||||||
bestSnapResult = endSnapResult;
|
bestSnapResult = endSnapResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
finalTime = bestSnapResult.snappedTime;
|
// Only use element snapping if it found a snap point, otherwise keep frame-snapped time
|
||||||
snapPoint = bestSnapResult.snapPoint;
|
if (bestSnapResult.snapPoint) {
|
||||||
|
finalTime = bestSnapResult.snappedTime;
|
||||||
|
snapPoint = bestSnapResult.snapPoint;
|
||||||
|
}
|
||||||
|
|
||||||
// Notify parent component about snap point change
|
// Notify parent component about snap point change
|
||||||
onSnapPointChange?.(snapPoint);
|
onSnapPointChange?.(snapPoint);
|
||||||
} else {
|
} else {
|
||||||
// Use frame snapping if project has FPS, otherwise use decimal snapping
|
// Clear snap point when element snapping is disabled
|
||||||
const projectStore = useProjectStore.getState();
|
|
||||||
const projectFps = projectStore.activeProject?.fps || 30;
|
|
||||||
finalTime = snapTimeToFrame(adjustedTime, projectFps);
|
|
||||||
|
|
||||||
// Clear snap point when not snapping
|
|
||||||
onSnapPointChange?.(null);
|
onSnapPointChange?.(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -221,17 +221,34 @@ export function useTimelineElementResize({
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Normal trimming within original duration
|
// Normal trimming within original duration
|
||||||
const maxTrimEnd = element.duration - resizing.initialTrimStart - 0.1; // Leave at least 0.1s visible
|
// Calculate the desired end time based on mouse movement
|
||||||
const newTrimEnd = snapTimeToFrame(
|
const currentEndTime =
|
||||||
Math.max(0, Math.min(maxTrimEnd, calculated)),
|
element.startTime +
|
||||||
projectFps
|
element.duration -
|
||||||
|
element.trimStart -
|
||||||
|
element.trimEnd;
|
||||||
|
const desiredEndTime = currentEndTime + deltaTime;
|
||||||
|
|
||||||
|
// Snap the desired end time to frame
|
||||||
|
const snappedEndTime = snapTimeToFrame(desiredEndTime, projectFps);
|
||||||
|
|
||||||
|
// Calculate what trimEnd should be to achieve this snapped end time
|
||||||
|
const newTrimEnd = Math.max(
|
||||||
|
0,
|
||||||
|
element.duration -
|
||||||
|
element.trimStart -
|
||||||
|
(snappedEndTime - element.startTime)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Ensure we don't trim more than available content (leave at least 0.1s visible)
|
||||||
|
const maxTrimEnd = element.duration - element.trimStart - 0.1;
|
||||||
|
const finalTrimEnd = Math.min(maxTrimEnd, newTrimEnd);
|
||||||
|
|
||||||
updateElementTrim(
|
updateElementTrim(
|
||||||
track.id,
|
track.id,
|
||||||
element.id,
|
element.id,
|
||||||
resizing.initialTrimStart,
|
element.trimStart,
|
||||||
newTrimEnd,
|
finalTrimEnd,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user