diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-20 11:31:17 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-20 11:31:17 +0200 |
| commit | 3e89d2998588f8e35c524e523ba2ee853f0f6475 (patch) | |
| tree | 57221f96c1259b3b07e3ddf8c1adefc502b9d8eb /apps/web/src/timeline/snapping | |
| parent | 729d10592f4e802f91c555c309ff3ba4b119f843 (diff) | |
| download | opencut-3e89d2998588f8e35c524e523ba2ee853f0f6475.tar.gz | |
refactor: restructure files to their domains, new preview overlay system, and dep graph
Diffstat (limited to 'apps/web/src/timeline/snapping')
| -rw-r--r-- | apps/web/src/timeline/snapping/build.ts | 17 | ||||
| -rw-r--r-- | apps/web/src/timeline/snapping/index.ts | 9 | ||||
| -rw-r--r-- | apps/web/src/timeline/snapping/resolve.ts | 28 | ||||
| -rw-r--r-- | apps/web/src/timeline/snapping/threshold.ts | 15 | ||||
| -rw-r--r-- | apps/web/src/timeline/snapping/types.ts | 21 |
5 files changed, 90 insertions, 0 deletions
diff --git a/apps/web/src/timeline/snapping/build.ts b/apps/web/src/timeline/snapping/build.ts new file mode 100644 index 00000000..87d3db2e --- /dev/null +++ b/apps/web/src/timeline/snapping/build.ts @@ -0,0 +1,17 @@ +import type { SnapPoint, TimelineSnapPointSource } from "./types"; + +export function buildTimelineSnapPoints({ + sources, +}: { + sources: TimelineSnapPointSource[]; +}): SnapPoint[] { + const snapPoints: SnapPoint[] = []; + + for (const source of sources) { + for (const snapPoint of source()) { + snapPoints.push(snapPoint); + } + } + + return snapPoints; +} diff --git a/apps/web/src/timeline/snapping/index.ts b/apps/web/src/timeline/snapping/index.ts new file mode 100644 index 00000000..73f49272 --- /dev/null +++ b/apps/web/src/timeline/snapping/index.ts @@ -0,0 +1,9 @@ +export { buildTimelineSnapPoints } from "./build"; +export { resolveTimelineSnap } from "./resolve"; +export { getTimelineSnapThresholdInTicks } from "./threshold"; +export type { + SnapPoint, + SnapPointType, + SnapResult, + TimelineSnapPointSource, +} from "./types"; diff --git a/apps/web/src/timeline/snapping/resolve.ts b/apps/web/src/timeline/snapping/resolve.ts new file mode 100644 index 00000000..2f00d081 --- /dev/null +++ b/apps/web/src/timeline/snapping/resolve.ts @@ -0,0 +1,28 @@ +import type { SnapPoint, SnapResult } from "./types"; + +export function resolveTimelineSnap({ + targetTime, + snapPoints, + maxSnapDistance, +}: { + targetTime: number; + snapPoints: SnapPoint[]; + maxSnapDistance: number; +}): SnapResult { + let closestSnapPoint: SnapPoint | null = null; + let closestDistance = Infinity; + + for (const snapPoint of snapPoints) { + const distance = Math.abs(targetTime - snapPoint.time); + if (distance <= maxSnapDistance && distance < closestDistance) { + closestDistance = distance; + closestSnapPoint = snapPoint; + } + } + + return { + snappedTime: closestSnapPoint ? closestSnapPoint.time : targetTime, + snapPoint: closestSnapPoint, + snapDistance: closestDistance, + }; +} diff --git a/apps/web/src/timeline/snapping/threshold.ts b/apps/web/src/timeline/snapping/threshold.ts new file mode 100644 index 00000000..f03111dc --- /dev/null +++ b/apps/web/src/timeline/snapping/threshold.ts @@ -0,0 +1,15 @@ +import { BASE_TIMELINE_PIXELS_PER_SECOND } from "@/timeline/scale"; +import { TICKS_PER_SECOND } from "@/wasm"; + +const DEFAULT_TIMELINE_SNAP_THRESHOLD_PX = 10; + +export function getTimelineSnapThresholdInTicks({ + zoomLevel, + snapThresholdPx = DEFAULT_TIMELINE_SNAP_THRESHOLD_PX, +}: { + zoomLevel: number; + snapThresholdPx?: number; +}): number { + const pixelsPerSecond = BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel; + return (snapThresholdPx / pixelsPerSecond) * TICKS_PER_SECOND; +} diff --git a/apps/web/src/timeline/snapping/types.ts b/apps/web/src/timeline/snapping/types.ts new file mode 100644 index 00000000..2a6d0903 --- /dev/null +++ b/apps/web/src/timeline/snapping/types.ts @@ -0,0 +1,21 @@ +export type SnapPointType = + | "element-start" + | "element-end" + | "playhead" + | "bookmark" + | "keyframe"; + +export interface SnapPoint { + time: number; + type: SnapPointType; + elementId?: string; + trackId?: string; +} + +export interface SnapResult { + snappedTime: number; + snapPoint: SnapPoint | null; + snapDistance: number; +} + +export type TimelineSnapPointSource = () => Iterable<SnapPoint>; |
