summaryrefslogtreecommitdiff
path: root/apps/web/src/lib/animation/vector-channel.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/lib/animation/vector-channel.ts')
-rw-r--r--apps/web/src/lib/animation/vector-channel.ts72
1 files changed, 72 insertions, 0 deletions
diff --git a/apps/web/src/lib/animation/vector-channel.ts b/apps/web/src/lib/animation/vector-channel.ts
new file mode 100644
index 00000000..50c62e40
--- /dev/null
+++ b/apps/web/src/lib/animation/vector-channel.ts
@@ -0,0 +1,72 @@
+import type {
+ AnimationPropertyPath,
+ ElementAnimations,
+ VectorAnimationChannel,
+ VectorValue,
+} from "@/lib/animation/types";
+
+export function isVectorValue(value: unknown): value is VectorValue {
+ return (
+ typeof value === "object" &&
+ value !== null &&
+ "x" in value &&
+ "y" in value &&
+ typeof (value as VectorValue).x === "number" &&
+ typeof (value as VectorValue).y === "number"
+ );
+}
+
+export function getVectorChannelForPath({
+ animations,
+ propertyPath,
+}: {
+ animations: ElementAnimations | undefined;
+ propertyPath: AnimationPropertyPath;
+}): VectorAnimationChannel | undefined {
+ const channel = animations?.channels[propertyPath];
+ if (!channel || channel.valueKind !== "vector") {
+ return undefined;
+ }
+ return channel;
+}
+
+export function getVectorChannelValueAtTime({
+ channel,
+ time,
+ fallbackValue,
+}: {
+ channel: VectorAnimationChannel | undefined;
+ time: number;
+ fallbackValue: VectorValue;
+}): VectorValue {
+ if (!channel || channel.keyframes.length === 0) {
+ return fallbackValue;
+ }
+
+ const keyframes = [...channel.keyframes].sort((a, b) => a.time - b.time);
+ const first = keyframes[0];
+ const last = keyframes[keyframes.length - 1];
+
+ if (!first || !last) return fallbackValue;
+ if (time <= first.time) return first.value;
+ if (time >= last.time) return last.value;
+
+ for (let i = 0; i < keyframes.length - 1; i++) {
+ const left = keyframes[i];
+ const right = keyframes[i + 1];
+ if (time < left.time || time > right.time) continue;
+
+ if (left.interpolation === "hold") return left.value;
+
+ const span = right.time - left.time;
+ if (span === 0) return right.value;
+
+ const t = (time - left.time) / span;
+ return {
+ x: left.value.x + (right.value.x - left.value.x) * t,
+ y: left.value.y + (right.value.y - left.value.y) * t,
+ };
+ }
+
+ return last.value;
+}