summaryrefslogtreecommitdiff
path: root/apps/web
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2025-08-01 09:51:54 +0200
committerMaze Winther <mazewinther@gmail.com>2025-08-01 09:51:54 +0200
commit09cb8c8d984e3e82f60e28b352508c876423fbdf (patch)
tree089b31e11f711eaa378ff39e1901674d835c1569 /apps/web
parentb00d6d5fb61229fcc035cc9680731f042d03b919 (diff)
downloadopencut-09cb8c8d984e3e82f60e28b352508c876423fbdf.tar.gz
fix: resize not matching with frames
Diffstat (limited to 'apps/web')
-rw-r--r--apps/web/src/components/editor/timeline/timeline-track.tsx96
-rw-r--r--apps/web/src/hooks/use-timeline-element-resize.ts29
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
);
}