summaryrefslogtreecommitdiff
path: root/apps/web/src/lib/animation/keyframe-query.ts
blob: 637b0289e60cf08ea87bfdc568b257f455a21ddc (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 { TIME_EPSILON_SECONDS } from "@/constants/animation-constants";
import type {
	AnimationPath,
	ElementAnimations,
	ElementKeyframe,
} from "@/lib/animation/types";
import { isAnimationPath } from "./target-resolver";

export function getElementKeyframes({
	animations,
}: {
	animations: ElementAnimations | undefined;
}): ElementKeyframe[] {
	if (!animations) {
		return [];
	}

	return Object.entries(animations.channels).flatMap(
		([propertyPath, channel]) => {
			if (
				!channel ||
				channel.keyframes.length === 0 ||
				!isAnimationPath(propertyPath)
			) {
				return [];
			}

			return channel.keyframes.map((keyframe) => ({
				propertyPath,
				id: keyframe.id,
				time: keyframe.time,
				value: keyframe.value,
				interpolation: keyframe.interpolation,
			}));
		},
	);
}

export function hasKeyframesForPath({
	animations,
	propertyPath,
}: {
	animations: ElementAnimations | undefined;
	propertyPath: AnimationPath;
}): boolean {
	const channel = animations?.channels[propertyPath];
	return Boolean(channel && channel.keyframes.length > 0);
}

export function getKeyframeAtTime({
	animations,
	propertyPath,
	time,
}: {
	animations: ElementAnimations | undefined;
	propertyPath: AnimationPath;
	time: number;
}): ElementKeyframe | null {
	const channel = animations?.channels[propertyPath];
	if (!channel || channel.keyframes.length === 0) return null;
	const keyframe = channel.keyframes.find(
		(keyframe) => Math.abs(keyframe.time - time) <= TIME_EPSILON_SECONDS,
	);
	if (!keyframe) return null;
	return {
		propertyPath,
		id: keyframe.id,
		time: keyframe.time,
		value: keyframe.value,
		interpolation: keyframe.interpolation,
	};
}