diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-07 01:09:13 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-07 01:09:13 +0200 |
| commit | e4b67094e757917588fb624a30aa046667403add (patch) | |
| tree | 55452692eac69a5a63b58f2a5cdb701dd7023d68 /apps/web/src/hooks/timeline/element/use-element-resize.ts | |
| parent | 79df73643185c02493364a077402726d1fa7b9ec (diff) | |
| download | opencut-e4b67094e757917588fb624a30aa046667403add.tar.gz | |
refactor: split effects and masks into dedicated rust crates, introduce MediaTime and FrameRate
Diffstat (limited to 'apps/web/src/hooks/timeline/element/use-element-resize.ts')
| -rw-r--r-- | apps/web/src/hooks/timeline/element/use-element-resize.ts | 42 |
1 files changed, 22 insertions, 20 deletions
diff --git a/apps/web/src/hooks/timeline/element/use-element-resize.ts b/apps/web/src/hooks/timeline/element/use-element-resize.ts index 3dd98bea..dec87eef 100644 --- a/apps/web/src/hooks/timeline/element/use-element-resize.ts +++ b/apps/web/src/hooks/timeline/element/use-element-resize.ts @@ -1,6 +1,7 @@ import { useState, useEffect, useRef, useCallback } from "react"; import { BASE_TIMELINE_PIXELS_PER_SECOND } from "@/lib/timeline/scale"; -import { snapTimeToFrame } from "opencut-wasm"; +import { TICKS_PER_SECOND } from "@/lib/wasm"; +import { roundToFrame } from "opencut-wasm"; import type { TimelineElement, TimelineTrack } from "@/lib/timeline"; import { useEditor } from "@/hooks/use-editor"; import { useShiftKey } from "@/hooks/use-shift-key"; @@ -185,13 +186,14 @@ export function useTimelineElementResize({ ({ clientX }: { clientX: number }) => { if (!resizing) return; - const deltaX = clientX - resizing.startX; - let deltaTime = - deltaX / (BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel); - let resizeSnapPoint: SnapPoint | null = null; + const deltaX = clientX - resizing.startX; + let deltaTime = Math.round( + (deltaX / (BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel)) * TICKS_PER_SECOND, + ); + let resizeSnapPoint: SnapPoint | null = null; - const projectFps = editor.project.getActive().settings.fps; - const minDurationSeconds = 1 / projectFps; + const projectFps = editor.project.getActive().settings.fps; + const minDuration = Math.round(TICKS_PER_SECOND * projectFps.denominator / projectFps.numerator); const shouldSnap = snappingEnabled && !isShiftHeldRef.current; if (shouldSnap) { const tracks = editor.timeline.getTracks(); @@ -277,19 +279,19 @@ export function useTimelineElementResize({ const maxAllowed = sourceDuration - resizing.initialTrimEnd - - getVisibleSourceSpanForDuration(minDurationSeconds); + getVisibleSourceSpanForDuration(minDuration); const calculated = resizing.initialTrimStart + getSourceDeltaForClipDelta(deltaTime); if (calculated >= 0 && calculated <= maxAllowed) { - const newTrimStart = snapTimeToFrame({ time: Math.min(maxAllowed, Math.max(minTrimStartForNeighbor, calculated)), fps: projectFps }); + const newTrimStart = roundToFrame({ time: Math.min(maxAllowed, Math.max(minTrimStartForNeighbor, calculated)), rate: projectFps }) ?? Math.min(maxAllowed, Math.max(minTrimStartForNeighbor, calculated)); const visibleSourceSpan = Math.max( 0, sourceDuration - newTrimStart - resizing.initialTrimEnd, ); - const newDuration = snapTimeToFrame({ time: getDurationForVisibleSourceSpan(visibleSourceSpan), fps: projectFps }); + const newDuration = roundToFrame({ time: getDurationForVisibleSourceSpan(visibleSourceSpan), rate: projectFps }) ?? getDurationForVisibleSourceSpan(visibleSourceSpan); const trimDelta = resizing.initialDuration - newDuration; - const newStartTime = snapTimeToFrame({ time: resizing.initialStartTime + trimDelta, fps: projectFps }); + const newStartTime = roundToFrame({ time: resizing.initialStartTime + trimDelta, rate: projectFps }) ?? resizing.initialStartTime + trimDelta; setCurrentTrimStart(newTrimStart); setCurrentStartTime(newStartTime); @@ -311,8 +313,8 @@ export function useTimelineElementResize({ ) : Math.min(extensionAmount, maxExtension), ); - const newStartTime = snapTimeToFrame({ time: resizing.initialStartTime - actualExtension, fps: projectFps }); - const newDuration = snapTimeToFrame({ time: resizing.initialDuration + actualExtension, fps: projectFps }); + const newStartTime = roundToFrame({ time: resizing.initialStartTime - actualExtension, rate: projectFps }) ?? resizing.initialStartTime - actualExtension; + const newDuration = roundToFrame({ time: resizing.initialDuration + actualExtension, rate: projectFps }) ?? resizing.initialDuration + actualExtension; setCurrentTrimStart(0); setCurrentStartTime(newStartTime); @@ -338,8 +340,8 @@ export function useTimelineElementResize({ 0, sourceDuration - newTrimStart - resizing.initialTrimEnd, ); - const newDuration = snapTimeToFrame({ time: getDurationForVisibleSourceSpan(visibleSourceSpan), fps: projectFps }); - const newStartTime = snapTimeToFrame({ time: resizing.initialStartTime + (resizing.initialDuration - newDuration), fps: projectFps }); + const newDuration = roundToFrame({ time: getDurationForVisibleSourceSpan(visibleSourceSpan), rate: projectFps }) ?? getDurationForVisibleSourceSpan(visibleSourceSpan); + const newStartTime = roundToFrame({ time: resizing.initialStartTime + (resizing.initialDuration - newDuration), rate: projectFps }) ?? resizing.initialStartTime + (resizing.initialDuration - newDuration); setCurrentTrimStart(newTrimStart); setCurrentStartTime(newStartTime); @@ -366,7 +368,7 @@ export function useTimelineElementResize({ const extensionNeeded = Math.abs(newTrimEnd); const baseDuration = resizing.initialDuration + resizing.initialTrimEnd; - const newDuration = snapTimeToFrame({ time: Math.min(baseDuration + extensionNeeded, maxAllowedDuration), fps: projectFps }); + const newDuration = roundToFrame({ time: Math.min(baseDuration + extensionNeeded, maxAllowedDuration), rate: projectFps }) ?? Math.min(baseDuration + extensionNeeded, maxAllowedDuration); setCurrentDuration(newDuration); setCurrentTrimEnd(0); @@ -376,7 +378,7 @@ export function useTimelineElementResize({ const unclampedDuration = getDurationForVisibleSourceSpan( Math.max(0, sourceDuration - resizing.initialTrimStart), ); - const newDuration = snapTimeToFrame({ time: Math.min(unclampedDuration, maxAllowedDuration), fps: projectFps }); + const newDuration = roundToFrame({ time: Math.min(unclampedDuration, maxAllowedDuration), rate: projectFps }) ?? Math.min(unclampedDuration, maxAllowedDuration); setCurrentDuration(newDuration); setCurrentTrimEnd(0); @@ -395,17 +397,17 @@ export function useTimelineElementResize({ const maxTrimEnd = sourceDuration - resizing.initialTrimStart - - getVisibleSourceSpanForDuration(minDurationSeconds); + getVisibleSourceSpanForDuration(minDuration); const clampedTrimEnd = Math.min( maxTrimEnd, Math.max(minTrimEndForNeighbor, newTrimEnd), ); - const finalTrimEnd = snapTimeToFrame({ time: clampedTrimEnd, fps: projectFps }); + const finalTrimEnd = roundToFrame({ time: clampedTrimEnd, rate: projectFps }) ?? clampedTrimEnd; const visibleSourceSpan = Math.max( 0, sourceDuration - resizing.initialTrimStart - finalTrimEnd, ); - const newDuration = snapTimeToFrame({ time: getDurationForVisibleSourceSpan(visibleSourceSpan), fps: projectFps }); + const newDuration = roundToFrame({ time: getDurationForVisibleSourceSpan(visibleSourceSpan), rate: projectFps }) ?? getDurationForVisibleSourceSpan(visibleSourceSpan); setCurrentTrimEnd(finalTrimEnd); setCurrentDuration(newDuration); |
