summaryrefslogtreecommitdiff
path: root/apps/web/src/types
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/types
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/types')
-rw-r--r--apps/web/src/types/animation.ts89
-rw-r--r--apps/web/src/types/timeline.ts13
2 files changed, 102 insertions, 0 deletions
diff --git a/apps/web/src/types/animation.ts b/apps/web/src/types/animation.ts
new file mode 100644
index 00000000..b9ca91af
--- /dev/null
+++ b/apps/web/src/types/animation.ts
@@ -0,0 +1,89 @@
+export const ANIMATION_PROPERTY_PATHS = [
+ "transform.position.x",
+ "transform.position.y",
+ "transform.scale",
+ "transform.rotate",
+ "opacity",
+ "volume",
+] as const;
+
+export type AnimationPropertyPath = (typeof ANIMATION_PROPERTY_PATHS)[number];
+
+export type AnimationValueKind = "number" | "color" | "discrete";
+export type DiscreteValue = boolean | string;
+export type AnimationValue = number | string | boolean;
+
+export type NumberKeyframeInterpolation = "linear" | "hold";
+export type ColorKeyframeInterpolation = "linear" | "hold";
+export type DiscreteKeyframeInterpolation = "hold";
+export type AnimationInterpolation =
+ | NumberKeyframeInterpolation
+ | ColorKeyframeInterpolation
+ | DiscreteKeyframeInterpolation;
+
+interface BaseAnimationKeyframe<
+ TValue extends AnimationValue,
+ TInterpolation extends AnimationInterpolation,
+> {
+ id: string;
+ time: number; // relative to element start time
+ value: TValue;
+ interpolation: TInterpolation;
+}
+
+export interface NumberKeyframe
+ extends BaseAnimationKeyframe<number, NumberKeyframeInterpolation> {}
+
+export interface ColorKeyframe
+ extends BaseAnimationKeyframe<string, ColorKeyframeInterpolation> {}
+
+export interface DiscreteKeyframe
+ extends BaseAnimationKeyframe<DiscreteValue, DiscreteKeyframeInterpolation> {}
+
+export type AnimationKeyframe = NumberKeyframe | ColorKeyframe | DiscreteKeyframe;
+
+interface BaseAnimationChannel<
+ TValueKind extends AnimationValueKind,
+ TKeyframe extends AnimationKeyframe,
+> {
+ valueKind: TValueKind;
+ keyframes: TKeyframe[];
+}
+
+export interface NumberAnimationChannel
+ extends BaseAnimationChannel<"number", NumberKeyframe> {}
+
+export interface ColorAnimationChannel
+ extends BaseAnimationChannel<"color", ColorKeyframe> {}
+
+export interface DiscreteAnimationChannel
+ extends BaseAnimationChannel<"discrete", DiscreteKeyframe> {}
+
+export type AnimationChannel =
+ | NumberAnimationChannel
+ | ColorAnimationChannel
+ | DiscreteAnimationChannel;
+
+export type ElementAnimationChannelMap = Record<
+ string,
+ AnimationChannel | undefined
+>;
+
+export interface ElementAnimations {
+ channels: ElementAnimationChannelMap;
+}
+
+export interface ElementKeyframe {
+ propertyPath: AnimationPropertyPath;
+ id: string;
+ time: number;
+ value: AnimationValue;
+ interpolation: AnimationInterpolation;
+}
+
+export interface SelectedKeyframeRef {
+ trackId: string;
+ elementId: string;
+ propertyPath: AnimationPropertyPath;
+ keyframeId: string;
+}
diff --git a/apps/web/src/types/timeline.ts b/apps/web/src/types/timeline.ts
index 5a98bd31..26690458 100644
--- a/apps/web/src/types/timeline.ts
+++ b/apps/web/src/types/timeline.ts
@@ -1,4 +1,5 @@
import type { BlendMode, Transform } from "./rendering";
+import type { ElementAnimations } from "./animation";
export interface Bookmark {
time: number;
@@ -80,6 +81,7 @@ interface BaseTimelineElement {
startTime: number;
trimStart: number;
trimEnd: number;
+ animations?: ElementAnimations;
}
export interface VideoElement extends BaseTimelineElement {
@@ -136,6 +138,17 @@ export interface StickerElement extends BaseTimelineElement {
blendMode?: BlendMode;
}
+export type VisualElement =
+ | VideoElement
+ | ImageElement
+ | TextElement
+ | StickerElement;
+
+export type ElementUpdatePatch =
+ | { transform: Transform }
+ | { opacity: number }
+ | { volume: number };
+
export type TimelineElement =
| AudioElement
| VideoElement