summaryrefslogtreecommitdiff
path: root/apps/web/src/animation/resolve.ts
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-29 10:37:39 +0200
committerMaze Winther <mazewinther@gmail.com>2026-05-02 03:27:23 +0200
commit401cc35743aafc4c8bdfb8055501650390bde88b (patch)
tree98385fb2f252ff6bb227a844d96b664a8fe87835 /apps/web/src/animation/resolve.ts
parentb8d08df244b97b1d3131cb2d076166838189eeb0 (diff)
downloadopencut-401cc35743aafc4c8bdfb8055501650390bde88b.tar.gz
refactor: animate params over time
Diffstat (limited to 'apps/web/src/animation/resolve.ts')
-rw-r--r--apps/web/src/animation/resolve.ts146
1 files changed, 110 insertions, 36 deletions
diff --git a/apps/web/src/animation/resolve.ts b/apps/web/src/animation/resolve.ts
index 19685cea..331dcbaa 100644
--- a/apps/web/src/animation/resolve.ts
+++ b/apps/web/src/animation/resolve.ts
@@ -1,15 +1,13 @@
import type {
AnimationPath,
- AnimationValueForPath,
ElementAnimations,
} from "@/animation/types";
-import {
- type AnimationComponentValue,
- composeAnimationValue,
- decomposeAnimationValue,
-} from "./binding-values";
+import { formatLinearRgba, parseColorToLinearRgba } from "@/params";
+import type { ParamValue } from "@/params";
+import { isCompositeChannelData, isLeafChannelData } from "./channel-data";
import {
getChannelValueAtTime,
+ isScalarChannel,
} from "./interpolation";
export function getElementLocalTime({
@@ -33,47 +31,123 @@ export function getElementLocalTime({
return localTime;
}
-export function resolveAnimationPathValueAtTime<TPath extends AnimationPath>({
+export function resolveAnimationPathValueAtTime({
+ animations,
+ propertyPath,
+ localTime,
+ fallbackValue,
+}: {
+ animations: ElementAnimations | undefined;
+ propertyPath: AnimationPath;
+ localTime: number;
+ fallbackValue: number;
+}): number;
+export function resolveAnimationPathValueAtTime({
+ animations,
+ propertyPath,
+ localTime,
+ fallbackValue,
+}: {
+ animations: ElementAnimations | undefined;
+ propertyPath: AnimationPath;
+ localTime: number;
+ fallbackValue: string;
+}): string;
+export function resolveAnimationPathValueAtTime({
+ animations,
+ propertyPath,
+ localTime,
+ fallbackValue,
+}: {
+ animations: ElementAnimations | undefined;
+ propertyPath: AnimationPath;
+ localTime: number;
+ fallbackValue: boolean;
+}): boolean;
+export function resolveAnimationPathValueAtTime({
+ animations,
+ propertyPath,
+ localTime,
+ fallbackValue,
+}: {
+ animations: ElementAnimations | undefined;
+ propertyPath: AnimationPath;
+ localTime: number;
+ fallbackValue: ParamValue;
+}): ParamValue;
+export function resolveAnimationPathValueAtTime({
animations,
propertyPath,
localTime,
fallbackValue,
}: {
animations: ElementAnimations | undefined;
- propertyPath: TPath;
+ propertyPath: AnimationPath;
localTime: number;
- fallbackValue: AnimationValueForPath<TPath>;
-}): AnimationValueForPath<TPath> {
- const binding = animations?.bindings[propertyPath];
- if (!binding) {
+ fallbackValue: ParamValue;
+}): ParamValue {
+ const data = animations?.[propertyPath];
+ if (!data) {
+ return fallbackValue;
+ }
+ if (isLeafChannelData(data)) {
+ if (typeof fallbackValue === "number") {
+ return getChannelValueAtTime({
+ channel: isScalarChannel(data) ? data : undefined,
+ time: localTime,
+ fallbackValue,
+ });
+ }
+ if (typeof fallbackValue === "string" || typeof fallbackValue === "boolean") {
+ return getChannelValueAtTime({
+ channel: !isScalarChannel(data) ? data : undefined,
+ time: localTime,
+ fallbackValue,
+ });
+ }
+ return fallbackValue;
+ }
+ if (!isCompositeChannelData(data)) {
return fallbackValue;
}
- const fallbackComponents = decomposeAnimationValue({
- kind: binding.kind,
- value: fallbackValue,
- });
- if (!fallbackComponents) {
+ if (
+ typeof fallbackValue !== "string" ||
+ !("r" in data) ||
+ !("g" in data) ||
+ !("b" in data) ||
+ !("a" in data)
+ ) {
return fallbackValue;
}
- const componentValues = Object.fromEntries(
- binding.components.map((component) => {
- const channel = animations?.channels[component.channelId];
- return [
- component.key,
- getChannelValueAtTime({
- channel,
- time: localTime,
- fallbackValue:
- fallbackComponents[component.key] ??
- (channel?.kind === "discrete" ? false : 0),
- }),
- ];
- }),
- ) as Record<string, AnimationComponentValue | undefined>;
- return (composeAnimationValue({
- binding,
- componentValues,
- }) ?? fallbackValue) as AnimationValueForPath<TPath>;
+ const fallbackComponents = parseColorToLinearRgba({ color: fallbackValue });
+ if (fallbackComponents === null) {
+ return fallbackValue;
+ }
+
+ return formatLinearRgba({
+ color: {
+ r: getChannelValueAtTime({
+ channel: data.r && isScalarChannel(data.r) ? data.r : undefined,
+ time: localTime,
+ fallbackValue: fallbackComponents.r,
+ }),
+ g: getChannelValueAtTime({
+ channel: data.g && isScalarChannel(data.g) ? data.g : undefined,
+ time: localTime,
+ fallbackValue: fallbackComponents.g,
+ }),
+ b: getChannelValueAtTime({
+ channel: data.b && isScalarChannel(data.b) ? data.b : undefined,
+ time: localTime,
+ fallbackValue: fallbackComponents.b,
+ }),
+ a: getChannelValueAtTime({
+ channel: data.a && isScalarChannel(data.a) ? data.a : undefined,
+ time: localTime,
+ fallbackValue: fallbackComponents.a,
+ }),
+ },
+ });
}