summaryrefslogtreecommitdiff
path: root/apps/web/src/lib/timeline/snap-utils.ts
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-02-27 16:33:57 +0100
committerMaze Winther <mazewinther@gmail.com>2026-02-27 16:33:57 +0100
commit49426f19cd8eaed7cf6c47999b66fee7ee637a56 (patch)
tree1b37451529cfcb36e6cae79494c61b9c616077df /apps/web/src/lib/timeline/snap-utils.ts
parent9b94f89def4f96836c4f42a23a73e97fe647a43c (diff)
downloadopencut-49426f19cd8eaed7cf6c47999b66fee7ee637a56.tar.gz
feat: implement keyframe animation system
Add keyframe support for transform, opacity, and volume properties. Includes animation engine (interpolation, mutations, resolvers), timeline markers with selection/snapping, properties panel toggles, keyframe-aware renderer, and full undo/redo command support. Also refactor element command constructors to object params, extract timeline pixel math to pixel-utils.ts, and update cursor rules.
Diffstat (limited to 'apps/web/src/lib/timeline/snap-utils.ts')
-rw-r--r--apps/web/src/lib/timeline/snap-utils.ts27
1 files changed, 22 insertions, 5 deletions
diff --git a/apps/web/src/lib/timeline/snap-utils.ts b/apps/web/src/lib/timeline/snap-utils.ts
index a29737d0..c7fac090 100644
--- a/apps/web/src/lib/timeline/snap-utils.ts
+++ b/apps/web/src/lib/timeline/snap-utils.ts
@@ -1,10 +1,11 @@
import type { Bookmark, TimelineTrack } from "@/types/timeline";
import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
import { BOOKMARK_TIME_EPSILON } from "@/lib/timeline/bookmarks";
+import { getElementKeyframes } from "@/lib/animation";
export interface SnapPoint {
time: number;
- type: "element-start" | "element-end" | "playhead" | "bookmark";
+ type: "element-start" | "element-end" | "playhead" | "bookmark" | "keyframe";
elementId?: string;
trackId?: string;
}
@@ -26,6 +27,7 @@ export function findSnapPoints({
enableElementSnapping = true,
enablePlayheadSnapping = true,
enableBookmarkSnapping = true,
+ enableKeyframeSnapping = true,
}: {
tracks: Array<TimelineTrack>;
playheadTime: number;
@@ -35,13 +37,15 @@ export function findSnapPoints({
enableElementSnapping?: boolean;
enablePlayheadSnapping?: boolean;
enableBookmarkSnapping?: boolean;
+ enableKeyframeSnapping?: boolean;
}): SnapPoint[] {
const snapPoints: SnapPoint[] = [];
- if (enableElementSnapping) {
- for (const track of tracks) {
- for (const element of track.elements) {
- if (element.id === excludeElementId) continue;
+ for (const track of tracks) {
+ for (const element of track.elements) {
+ if (element.id === excludeElementId) continue;
+
+ if (enableElementSnapping) {
snapPoints.push(
{
time: element.startTime,
@@ -57,6 +61,19 @@ export function findSnapPoints({
},
);
}
+
+ if (enableKeyframeSnapping) {
+ for (const keyframe of getElementKeyframes({
+ animations: element.animations,
+ })) {
+ snapPoints.push({
+ time: element.startTime + keyframe.time,
+ type: "keyframe",
+ elementId: element.id,
+ trackId: track.id,
+ });
+ }
+ }
}
}