summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2025-07-31 23:47:10 +0200
committerMaze Winther <mazewinther@gmail.com>2025-07-31 23:47:10 +0200
commitb00d6d5fb61229fcc035cc9680731f042d03b919 (patch)
tree10efe1dc63dc33f70f78359c93ed2026fa74e52a /apps
parent60d4b0c3d6a0cc69f165f7db8ad5099b3d1589ca (diff)
downloadopencut-b00d6d5fb61229fcc035cc9680731f042d03b919.tar.gz
fix: make timeline element resizing frame based instead of pixel based
Diffstat (limited to 'apps')
-rw-r--r--apps/web/src/hooks/use-timeline-element-resize.ts41
1 files changed, 34 insertions, 7 deletions
diff --git a/apps/web/src/hooks/use-timeline-element-resize.ts b/apps/web/src/hooks/use-timeline-element-resize.ts
index e7d46dff..d57533e0 100644
--- a/apps/web/src/hooks/use-timeline-element-resize.ts
+++ b/apps/web/src/hooks/use-timeline-element-resize.ts
@@ -2,6 +2,8 @@ import { useState, useEffect } from "react";
import { ResizeState, TimelineElement, TimelineTrack } from "@/types/timeline";
import { useMediaStore } from "@/stores/media-store";
import { useTimelineStore } from "@/stores/timeline-store";
+import { useProjectStore } from "@/stores/project-store";
+import { snapTimeToFrame } from "@/constants/timeline-constants";
interface UseTimelineElementResizeProps {
element: TimelineElement;
@@ -109,6 +111,10 @@ export function useTimelineElementResize({
// Reasonable sensitivity for resize operations - similar to timeline scale
const deltaTime = deltaX / (50 * zoomLevel);
+ // Get project FPS for frame snapping
+ const projectStore = useProjectStore.getState();
+ const projectFps = projectStore.activeProject?.fps || 30;
+
if (resizing.side === "left") {
// Left resize - different behavior for media vs text/image elements
const maxAllowed = element.duration - resizing.initialTrimEnd - 0.1;
@@ -116,9 +122,15 @@ export function useTimelineElementResize({
if (calculated >= 0) {
// Normal trimming within available content
- const newTrimStart = Math.min(maxAllowed, calculated);
+ const newTrimStart = snapTimeToFrame(
+ Math.min(maxAllowed, calculated),
+ projectFps
+ );
const trimDelta = newTrimStart - resizing.initialTrimStart;
- const newStartTime = element.startTime + trimDelta;
+ const newStartTime = snapTimeToFrame(
+ element.startTime + trimDelta,
+ projectFps
+ );
updateElementTrim(
track.id,
@@ -135,8 +147,14 @@ export function useTimelineElementResize({
const extensionAmount = Math.abs(calculated);
const maxExtension = element.startTime;
const actualExtension = Math.min(extensionAmount, maxExtension);
- const newStartTime = element.startTime - actualExtension;
- const newDuration = element.duration + actualExtension;
+ const newStartTime = snapTimeToFrame(
+ element.startTime - actualExtension,
+ projectFps
+ );
+ const newDuration = snapTimeToFrame(
+ element.duration + actualExtension,
+ projectFps
+ );
// Keep trimStart at 0 and extend the element
updateElementTrim(
@@ -152,7 +170,10 @@ export function useTimelineElementResize({
// Video/Audio: can't extend beyond original content - limit to trimStart = 0
const newTrimStart = 0;
const trimDelta = newTrimStart - resizing.initialTrimStart;
- const newStartTime = element.startTime + trimDelta;
+ const newStartTime = snapTimeToFrame(
+ element.startTime + trimDelta,
+ projectFps
+ );
updateElementTrim(
track.id,
@@ -173,7 +194,10 @@ export function useTimelineElementResize({
if (canExtendElementDuration()) {
// Extend the duration instead of reducing trimEnd further
const extensionNeeded = Math.abs(calculated);
- const newDuration = element.duration + extensionNeeded;
+ const newDuration = snapTimeToFrame(
+ element.duration + extensionNeeded,
+ projectFps
+ );
const newTrimEnd = 0; // Reset trimEnd to 0 since we're extending
// Update duration first, then trim
@@ -198,7 +222,10 @@ 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 = Math.max(0, Math.min(maxTrimEnd, calculated));
+ const newTrimEnd = snapTimeToFrame(
+ Math.max(0, Math.min(maxTrimEnd, calculated)),
+ projectFps
+ );
updateElementTrim(
track.id,