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/animation/graph-channels.ts | |
| 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/animation/graph-channels.ts')
| -rw-r--r-- | apps/web/src/animation/graph-channels.ts | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/apps/web/src/animation/graph-channels.ts b/apps/web/src/animation/graph-channels.ts new file mode 100644 index 00000000..d6482602 --- /dev/null +++ b/apps/web/src/animation/graph-channels.ts @@ -0,0 +1,99 @@ +import type { + AnimationBindingInstance, + AnimationPath, + ElementAnimations, + ScalarAnimationChannel, + ScalarGraphChannel, + ScalarGraphKeyframeContext, +} from "@/animation/types"; + +export interface EditableScalarChannels { + binding: AnimationBindingInstance; + channels: ScalarGraphChannel[]; +} + +function isScalarAnimationChannel( + channel: ElementAnimations["channels"][string], +): channel is ScalarAnimationChannel { + return channel?.kind === "scalar"; +} + +export function getEditableScalarChannels({ + animations, + propertyPath, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; +}): EditableScalarChannels | null { + const binding = animations?.bindings[propertyPath]; + if (!binding) { + return null; + } + + const channels = binding.components.flatMap((component) => { + const channel = animations?.channels[component.channelId]; + if (!isScalarAnimationChannel(channel)) { + return []; + } + + return [ + { + propertyPath, + componentKey: component.key, + channelId: component.channelId, + channel, + } satisfies ScalarGraphChannel, + ]; + }); + + return { binding, channels }; +} + +export function getEditableScalarChannel({ + animations, + propertyPath, + componentKey, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + componentKey: string; +}): ScalarGraphChannel | null { + const result = getEditableScalarChannels({ animations, propertyPath }); + return result?.channels.find((channel) => channel.componentKey === componentKey) ?? null; +} + +export function getScalarKeyframeContext({ + animations, + propertyPath, + componentKey, + keyframeId, +}: { + animations: ElementAnimations | undefined; + propertyPath: AnimationPath; + componentKey: string; + keyframeId: string; +}): ScalarGraphKeyframeContext | null { + const scalarChannel = getEditableScalarChannel({ + animations, + propertyPath, + componentKey, + }); + if (!scalarChannel) { + return null; + } + + const keyframeIndex = scalarChannel.channel.keys.findIndex( + (keyframe) => keyframe.id === keyframeId, + ); + if (keyframeIndex < 0) { + return null; + } + + return { + ...scalarChannel, + keyframe: scalarChannel.channel.keys[keyframeIndex], + keyframeIndex, + previousKey: scalarChannel.channel.keys[keyframeIndex - 1] ?? null, + nextKey: scalarChannel.channel.keys[keyframeIndex + 1] ?? null, + }; +} |
