fix: resize not matching with frames

This commit is contained in:
Maze Winther 2025-08-01 09:51:54 +02:00
parent b00d6d5fb6
commit 09cb8c8d98
2 changed files with 78 additions and 53 deletions

View File

@ -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);
// 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
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;
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);
}

View File

@ -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
);
}