summaryrefslogtreecommitdiff
path: root/apps/web/src/hooks/timeline/use-timeline-snapping.ts
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-02-23 10:55:35 +0100
committerMaze Winther <mazewinther@gmail.com>2026-02-23 10:55:35 +0100
commit4d77e3f2bb9884ad9e43761eaf0ede8c1a9cfccc (patch)
treecac5f7d98325504ec2c579eb0045e1adf1f83139 /apps/web/src/hooks/timeline/use-timeline-snapping.ts
parentab64c48c92f13434f988628a2e5ecf53063d0949 (diff)
downloadopencut-4d77e3f2bb9884ad9e43761eaf0ede8c1a9cfccc.tar.gz
refactor: snapping system
Diffstat (limited to 'apps/web/src/hooks/timeline/use-timeline-snapping.ts')
-rw-r--r--apps/web/src/hooks/timeline/use-timeline-snapping.ts185
1 files changed, 0 insertions, 185 deletions
diff --git a/apps/web/src/hooks/timeline/use-timeline-snapping.ts b/apps/web/src/hooks/timeline/use-timeline-snapping.ts
deleted file mode 100644
index 5de75448..00000000
--- a/apps/web/src/hooks/timeline/use-timeline-snapping.ts
+++ /dev/null
@@ -1,185 +0,0 @@
-import { useCallback } from "react";
-import type { Bookmark, TimelineTrack } from "@/types/timeline";
-import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
-import { BOOKMARK_TIME_EPSILON } from "@/lib/timeline/bookmarks";
-
-export interface SnapPoint {
- time: number;
- type: "element-start" | "element-end" | "playhead" | "bookmark";
- elementId?: string;
- trackId?: string;
-}
-
-export interface SnapResult {
- snappedTime: number;
- snapPoint: SnapPoint | null;
- snapDistance: number;
-}
-
-export interface UseTimelineSnappingOptions {
- snapThreshold?: number;
- enableElementSnapping?: boolean;
- enablePlayheadSnapping?: boolean;
- enableBookmarkSnapping?: boolean;
-}
-
-export function useTimelineSnapping({
- snapThreshold = 10,
- enableElementSnapping = true,
- enablePlayheadSnapping = true,
- enableBookmarkSnapping = true,
-}: UseTimelineSnappingOptions = {}) {
- const findSnapPoints = useCallback(
- ({
- tracks,
- playheadTime,
- excludeElementId,
- bookmarks = [],
- excludeBookmarkTime,
- }: {
- tracks: Array<TimelineTrack>;
- playheadTime: number;
- excludeElementId?: string;
- bookmarks?: Array<Bookmark>;
- excludeBookmarkTime?: number;
- }): SnapPoint[] => {
- const snapPoints: SnapPoint[] = [];
-
- if (enableElementSnapping) {
- for (const track of tracks) {
- for (const element of track.elements) {
- if (element.id === excludeElementId) continue;
-
- const elementStart = element.startTime;
- const elementEnd = element.startTime + element.duration;
-
- snapPoints.push(
- {
- time: elementStart,
- type: "element-start",
- elementId: element.id,
- trackId: track.id,
- },
- {
- time: elementEnd,
- type: "element-end",
- elementId: element.id,
- trackId: track.id,
- },
- );
- }
- }
- }
-
- if (enablePlayheadSnapping) {
- snapPoints.push({
- time: playheadTime,
- type: "playhead",
- });
- }
-
- if (enableBookmarkSnapping) {
- for (const bookmark of bookmarks) {
- if (
- excludeBookmarkTime != null &&
- Math.abs(bookmark.time - excludeBookmarkTime) <
- BOOKMARK_TIME_EPSILON
- ) {
- continue;
- }
- snapPoints.push({
- time: bookmark.time,
- type: "bookmark",
- });
- }
- }
-
- return snapPoints;
- },
- [enableElementSnapping, enablePlayheadSnapping, enableBookmarkSnapping],
- );
-
- const snapToNearestPoint = useCallback(
- ({
- targetTime,
- snapPoints,
- zoomLevel,
- }: {
- targetTime: number;
- snapPoints: Array<SnapPoint>;
- zoomLevel: number;
- }): SnapResult => {
- const pixelsPerSecond = TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel;
- const thresholdInSeconds = snapThreshold / pixelsPerSecond;
-
- let closestSnapPoint: SnapPoint | null = null;
- let closestDistance = Infinity;
-
- for (const snapPoint of snapPoints) {
- const distance = Math.abs(targetTime - snapPoint.time);
- if (distance < thresholdInSeconds && distance < closestDistance) {
- closestDistance = distance;
- closestSnapPoint = snapPoint;
- }
- }
-
- return {
- snappedTime: closestSnapPoint ? closestSnapPoint.time : targetTime,
- snapPoint: closestSnapPoint,
- snapDistance: closestDistance,
- };
- },
- [snapThreshold],
- );
-
- const snapElementEdge = useCallback(
- ({
- targetTime,
- elementDuration,
- tracks,
- playheadTime,
- zoomLevel,
- excludeElementId,
- snapToStart = true,
- bookmarks = [],
- }: {
- targetTime: number;
- elementDuration: number;
- tracks: Array<TimelineTrack>;
- playheadTime: number;
- zoomLevel: number;
- excludeElementId?: string;
- snapToStart?: boolean;
- bookmarks?: Array<Bookmark>;
- }): SnapResult => {
- const snapPoints = findSnapPoints({
- tracks,
- playheadTime,
- excludeElementId,
- bookmarks,
- });
-
- const effectiveTargetTime = snapToStart
- ? targetTime
- : targetTime + elementDuration;
- const snapResult = snapToNearestPoint({
- targetTime: effectiveTargetTime,
- snapPoints,
- zoomLevel,
- });
-
- if (!snapToStart && snapResult.snapPoint) {
- snapResult.snappedTime = snapResult.snappedTime - elementDuration;
- }
-
- return snapResult;
- },
- [findSnapPoints, snapToNearestPoint],
- );
-
- return {
- snapElementEdge,
- findSnapPoints,
- snapToNearestPoint,
- };
-}