diff options
| author | Maze Winther <mazewinther@gmail.com> | 2025-08-01 09:51:54 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2025-08-01 09:51:54 +0200 |
| commit | 09cb8c8d984e3e82f60e28b352508c876423fbdf (patch) | |
| tree | 089b31e11f711eaa378ff39e1901674d835c1569 /apps | |
| parent | b00d6d5fb61229fcc035cc9680731f042d03b919 (diff) | |
| download | opencut-09cb8c8d984e3e82f60e28b352508c876423fbdf.tar.gz | |
fix: resize not matching with frames
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/web/src/components/editor/timeline/timeline-track.tsx | 96 | ||||
| -rw-r--r-- | apps/web/src/hooks/use-timeline-element-resize.ts | 29 |
2 files changed, 75 insertions, 50 deletions
diff --git a/apps/web/src/components/editor/timeline/timeline-track.tsx b/apps/web/src/components/editor/timeline/timeline-track.tsx index 097fc051..1982d03e 100644 --- a/apps/web/src/components/editor/timeline/timeline-track.tsx +++ b/apps/web/src/components/editor/timeline/timeline-track.tsx @@ -68,45 +68,51 @@ export function TimelineTrackContent({ elementDuration: number, excludeElementId?: string ) => { - if (!snappingEnabled) { - // Use frame snapping if project has FPS, otherwise use decimal snapping - const projectStore = useProjectStore.getState(); - const projectFps = projectStore.activeProject?.fps || 30; - return snapTimeToFrame(dropTime, projectFps); - } + // Always apply frame snapping first + const projectStore = useProjectStore.getState(); + const projectFps = projectStore.activeProject?.fps || 30; + let finalTime = snapTimeToFrame(dropTime, projectFps); - // Try snapping both start and end edges for drops - const startSnapResult = snapElementEdge( - dropTime, - elementDuration, - tracks, - currentTime, - zoomLevel, - excludeElementId, - true // snap to start edge - ); + // 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 - ); + 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; + // 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; + } } - return bestSnapResult.snappedTime; + return finalTime; }; const timelineRef = useRef<HTMLDivElement>(null); @@ -148,9 +154,13 @@ export function TimelineTrackContent({ ); const adjustedTime = Math.max(0, mouseTime - dragState.clickOffsetTime); - // Apply snapping if enabled - let finalTime = adjustedTime; + // Always apply frame snapping first + const projectStore = useProjectStore.getState(); + const projectFps = projectStore.activeProject?.fps || 30; + let finalTime = snapTimeToFrame(adjustedTime, projectFps); let snapPoint = null; + + // Additionally apply element snapping if enabled if (snappingEnabled) { // Find the element being dragged to get its duration let elementDuration = 5; // fallback duration @@ -196,18 +206,16 @@ export function TimelineTrackContent({ bestSnapResult = endSnapResult; } - finalTime = bestSnapResult.snappedTime; - snapPoint = bestSnapResult.snapPoint; + // Only use element snapping if it found a snap point, otherwise keep frame-snapped time + if (bestSnapResult.snapPoint) { + finalTime = bestSnapResult.snappedTime; + snapPoint = bestSnapResult.snapPoint; + } // Notify parent component about snap point change onSnapPointChange?.(snapPoint); } else { - // Use frame snapping if project has FPS, otherwise use decimal snapping - const projectStore = useProjectStore.getState(); - const projectFps = projectStore.activeProject?.fps || 30; - finalTime = snapTimeToFrame(adjustedTime, projectFps); - - // Clear snap point when not snapping + // Clear snap point when element snapping is disabled onSnapPointChange?.(null); } diff --git a/apps/web/src/hooks/use-timeline-element-resize.ts b/apps/web/src/hooks/use-timeline-element-resize.ts index d57533e0..ba13e05a 100644 --- a/apps/web/src/hooks/use-timeline-element-resize.ts +++ b/apps/web/src/hooks/use-timeline-element-resize.ts @@ -221,17 +221,34 @@ export function useTimelineElementResize({ } } else { // Normal trimming within original duration - const maxTrimEnd = element.duration - resizing.initialTrimStart - 0.1; // Leave at least 0.1s visible - const newTrimEnd = snapTimeToFrame( - Math.max(0, Math.min(maxTrimEnd, calculated)), - projectFps + // Calculate the desired end time based on mouse movement + const currentEndTime = + element.startTime + + 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( track.id, element.id, - resizing.initialTrimStart, - newTrimEnd, + element.trimStart, + finalTrimEnd, false ); } |
