fix: clean up

This commit is contained in:
Maze Winther
2026-04-06 12:59:07 +02:00
parent 2776adaae1
commit c411edb7c1
8 changed files with 60 additions and 28 deletions
@@ -83,7 +83,7 @@ export function useKeyframedParamProperty({
defaultInterpolation: getParamDefaultInterpolation({ defaultInterpolation: getParamDefaultInterpolation({
param, param,
}), }),
coerceValue: (nextValue) => coerceValue: ({ value: nextValue }) =>
coerceAnimationValueForParam({ coerceAnimationValueForParam({
param, param,
value: nextValue, value: nextValue,
+3 -1
View File
@@ -1,5 +1,7 @@
import type { ScalarAnimationKey } from "@/lib/animation/types"; import type { ScalarAnimationKey } from "@/lib/animation/types";
const BEZIER_SOLVE_ITERATIONS = 20;
export function getBezierPoint({ export function getBezierPoint({
progress, progress,
p0, p0,
@@ -68,7 +70,7 @@ export function solveBezierProgressForTime({
const leftHandle = const leftHandle =
rightKey.leftHandle ?? getDefaultLeftHandle({ leftKey, rightKey }); rightKey.leftHandle ?? getDefaultLeftHandle({ leftKey, rightKey });
for (let iteration = 0; iteration < 20; iteration++) { for (let iteration = 0; iteration < BEZIER_SOLVE_ITERATIONS; iteration++) {
const mid = (lower + upper) / 2; const mid = (lower + upper) / 2;
const estimate = getBezierPoint({ const estimate = getBezierPoint({
progress: mid, progress: mid,
+4 -7
View File
@@ -13,6 +13,7 @@ import type {
Vector2AnimationBinding, Vector2AnimationBinding,
VectorValue, VectorValue,
} from "@/lib/animation/types"; } from "@/lib/animation/types";
import { clamp } from "@/utils/math";
interface LinearRgba { interface LinearRgba {
r: number; r: number;
@@ -25,10 +26,6 @@ export type AnimationComponentValue = number | DiscreteValue;
const toRgb = converter("rgb"); const toRgb = converter("rgb");
function clamp01({ value }: { value: number }): number {
return Math.max(0, Math.min(1, value));
}
function srgbToLinear({ value }: { value: number }): number { function srgbToLinear({ value }: { value: number }): number {
return value <= 0.04045 return value <= 0.04045
? value / 12.92 ? value / 12.92
@@ -36,7 +33,7 @@ function srgbToLinear({ value }: { value: number }): number {
} }
function linearToSrgb({ value }: { value: number }): number { function linearToSrgb({ value }: { value: number }): number {
const clamped = clamp01({ value }); const clamped = clamp({ value, min: 0, max: 1 });
return clamped <= 0.0031308 return clamped <= 0.0031308
? clamped * 12.92 ? clamped * 12.92
: 1.055 * Math.pow(clamped, 1 / 2.4) - 0.055; : 1.055 * Math.pow(clamped, 1 / 2.4) - 0.055;
@@ -234,7 +231,7 @@ export function parseColorToLinearRgba({
r: srgbToLinear({ value: rgb.r ?? 0 }), r: srgbToLinear({ value: rgb.r ?? 0 }),
g: srgbToLinear({ value: rgb.g ?? 0 }), g: srgbToLinear({ value: rgb.g ?? 0 }),
b: srgbToLinear({ value: rgb.b ?? 0 }), b: srgbToLinear({ value: rgb.b ?? 0 }),
a: clamp01({ value: rgb.alpha ?? 1 }), a: clamp({ value: rgb.alpha ?? 1, min: 0, max: 1 }),
}; };
} }
@@ -248,7 +245,7 @@ export function formatLinearRgba({
r: linearToSrgb({ value: color.r }), r: linearToSrgb({ value: color.r }),
g: linearToSrgb({ value: color.g }), g: linearToSrgb({ value: color.g }),
b: linearToSrgb({ value: color.b }), b: linearToSrgb({ value: color.b }),
alpha: clamp01({ value: color.a }), alpha: clamp({ value: color.a, min: 0, max: 1 }),
} as const; } as const;
return rgb.alpha < 1 ? formatHex8(rgb) : formatHex(rgb); return rgb.alpha < 1 ? formatHex8(rgb) : formatHex(rgb);
} }
+4 -5
View File
@@ -9,6 +9,7 @@ import type {
ScalarSegmentType, ScalarSegmentType,
} from "@/lib/animation/types"; } from "@/lib/animation/types";
import { TIME_EPSILON_SECONDS } from "@/constants/animation-constants"; import { TIME_EPSILON_SECONDS } from "@/constants/animation-constants";
import { clamp } from "@/utils/math";
import { import {
getBezierPoint, getBezierPoint,
getDefaultLeftHandle, getDefaultLeftHandle,
@@ -41,10 +42,6 @@ function isWithinTimePair({
); );
} }
function clamp01({ value }: { value: number }): number {
return Math.max(0, Math.min(1, value));
}
function lerpNumber({ function lerpNumber({
leftValue, leftValue,
rightValue, rightValue,
@@ -287,8 +284,10 @@ export function getScalarChannelValueAtTime({
return rightKey.value; return rightKey.value;
} }
const progress = clamp01({ const progress = clamp({
value: (time - leftKey.time) / span, value: (time - leftKey.time) / span,
min: 0,
max: 1,
}); });
if (leftKey.segmentToNext === "linear") { if (leftKey.segmentToNext === "linear") {
return lerpNumber({ return lerpNumber({
+3 -3
View File
@@ -440,9 +440,9 @@ export function upsertPathKeyframe({
keyframeId?: string; keyframeId?: string;
kind: AnimationBindingKind; kind: AnimationBindingKind;
defaultInterpolation: AnimationInterpolation; defaultInterpolation: AnimationInterpolation;
coerceValue: (value: AnimationValue) => AnimationValue | null; coerceValue: ({ value }: { value: AnimationValue }) => AnimationValue | null;
}): ElementAnimations | undefined { }): ElementAnimations | undefined {
const coercedValue = coerceValue(value); const coercedValue = coerceValue({ value });
if (coercedValue === null) { if (coercedValue === null) {
return animations; return animations;
} }
@@ -547,7 +547,7 @@ export function upsertElementKeyframe({
keyframeId, keyframeId,
kind: propertyDefinition.kind, kind: propertyDefinition.kind,
defaultInterpolation: propertyDefinition.defaultInterpolation, defaultInterpolation: propertyDefinition.defaultInterpolation,
coerceValue: (nextValue) => coerceValue: ({ value: nextValue }) =>
coerceAnimationValueForProperty({ coerceAnimationValueForProperty({
propertyPath, propertyPath,
value: nextValue, value: nextValue,
@@ -32,9 +32,9 @@ export interface AnimationPathDescriptor {
kind: AnimationBindingKind; kind: AnimationBindingKind;
defaultInterpolation: AnimationInterpolation; defaultInterpolation: AnimationInterpolation;
numericRanges?: Partial<Record<string, NumericSpec>>; numericRanges?: Partial<Record<string, NumericSpec>>;
coerceValue(value: AnimationValue): AnimationValue | null; coerceValue: ({ value }: { value: AnimationValue }) => AnimationValue | null;
getBaseValue(): AnimationValue | null; getBaseValue: () => AnimationValue | null;
setBaseValue(value: AnimationValue): TimelineElement; setBaseValue: ({ value }: { value: AnimationValue }) => TimelineElement;
} }
export function getParamValueKind({ export function getParamValueKind({
@@ -135,9 +135,9 @@ function buildGraphicParamDescriptor({
kind: getParamValueKind({ param }), kind: getParamValueKind({ param }),
defaultInterpolation: getParamDefaultInterpolation({ param }), defaultInterpolation: getParamDefaultInterpolation({ param }),
numericRanges: getParamNumericRange({ param }), numericRanges: getParamNumericRange({ param }),
coerceValue: (value) => coerceAnimationValueForParam({ param, value }), coerceValue: ({ value }) => coerceAnimationValueForParam({ param, value }),
getBaseValue: () => element.params[param.key] ?? param.default, getBaseValue: () => element.params[param.key] ?? param.default,
setBaseValue: (value) => { setBaseValue: ({ value }) => {
const coercedValue = coerceAnimationValueForParam({ param, value }); const coercedValue = coerceAnimationValueForParam({ param, value });
if (coercedValue === null) { if (coercedValue === null) {
return element; return element;
@@ -183,9 +183,9 @@ function buildEffectParamDescriptor({
kind: getParamValueKind({ param }), kind: getParamValueKind({ param }),
defaultInterpolation: getParamDefaultInterpolation({ param }), defaultInterpolation: getParamDefaultInterpolation({ param }),
numericRanges: getParamNumericRange({ param }), numericRanges: getParamNumericRange({ param }),
coerceValue: (value) => coerceAnimationValueForParam({ param, value }), coerceValue: ({ value }) => coerceAnimationValueForParam({ param, value }),
getBaseValue: () => effect.params[param.key] ?? param.default, getBaseValue: () => effect.params[param.key] ?? param.default,
setBaseValue: (value) => { setBaseValue: ({ value }) => {
const coercedValue = coerceAnimationValueForParam({ param, value }); const coercedValue = coerceAnimationValueForParam({ param, value });
if (coercedValue === null) { if (coercedValue === null) {
return element; return element;
@@ -239,7 +239,7 @@ export function resolveAnimationTarget({
kind: propertyDefinition.kind, kind: propertyDefinition.kind,
defaultInterpolation: propertyDefinition.defaultInterpolation, defaultInterpolation: propertyDefinition.defaultInterpolation,
numericRanges: propertyDefinition.numericRanges, numericRanges: propertyDefinition.numericRanges,
coerceValue: (value) => coerceValue: ({ value }) =>
coerceAnimationValueForProperty({ coerceAnimationValueForProperty({
propertyPath: path, propertyPath: path,
value, value,
@@ -249,7 +249,7 @@ export function resolveAnimationTarget({
element, element,
propertyPath: path, propertyPath: path,
}), }),
setBaseValue: (value) => { setBaseValue: ({ value }) => {
const coercedValue = propertyDefinition.coerceValue({ value }); const coercedValue = propertyDefinition.coerceValue({ value });
if (coercedValue === null) { if (coercedValue === null) {
return element; return element;
@@ -79,7 +79,7 @@ function removeKeyframeAndPersist({
const shouldPersistToBase = isChannelNowEmpty && valueBefore !== null; const shouldPersistToBase = isChannelNowEmpty && valueBefore !== null;
const baseElement = shouldPersistToBase const baseElement = shouldPersistToBase
? target.setBaseValue(valueBefore) ? target.setBaseValue({ value: valueBefore })
: element; : element;
return { ...baseElement, animations: nextAnimations }; return { ...baseElement, animations: nextAnimations };
@@ -1,10 +1,44 @@
import { parseColorToLinearRgba } from "@/lib/animation/binding-values"; import { converter, parse } from "culori";
import type { MigrationResult, ProjectRecord } from "./types"; import type { MigrationResult, ProjectRecord } from "./types";
import { getProjectId, isRecord } from "./utils"; import { getProjectId, isRecord } from "./utils";
const COLOR_COMPONENT_KEYS = ["r", "g", "b", "a"] as const; const COLOR_COMPONENT_KEYS = ["r", "g", "b", "a"] as const;
type LegacyInterpolation = "linear" | "hold"; type LegacyInterpolation = "linear" | "hold";
const toRgb = converter("rgb");
interface LinearRgba {
r: number;
g: number;
b: number;
a: number;
}
function srgbToLinear({ value }: { value: number }): number {
return value <= 0.04045
? value / 12.92
: Math.pow((value + 0.055) / 1.055, 2.4);
}
function parseColorToLinearRgba({
color,
}: {
color: string;
}): LinearRgba | null {
const parsed = parse(color);
const rgb = parsed ? toRgb(parsed) : null;
if (!rgb) {
return null;
}
return {
r: srgbToLinear({ value: rgb.r ?? 0 }),
g: srgbToLinear({ value: rgb.g ?? 0 }),
b: srgbToLinear({ value: rgb.b ?? 0 }),
a: Math.max(0, Math.min(1, rgb.alpha ?? 1)),
};
}
interface LegacyScalarKeyframe { interface LegacyScalarKeyframe {
id: string; id: string;
time: number; time: number;