summaryrefslogtreecommitdiff
path: root/apps/web/src/lib/animation/vector-channel.ts
blob: f7f65049c385b610ed36144ddec0d3a4ee8abcd9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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;
}