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

View File

@ -83,7 +83,7 @@ export function useKeyframedParamProperty({
defaultInterpolation: getParamDefaultInterpolation({
param,
}),
coerceValue: (nextValue) =>
coerceValue: ({ value: nextValue }) =>
coerceAnimationValueForParam({
param,
value: nextValue,

View File

@ -1,5 +1,7 @@
import type { ScalarAnimationKey } from "@/lib/animation/types";
const BEZIER_SOLVE_ITERATIONS = 20;
export function getBezierPoint({
progress,
p0,
@ -68,7 +70,7 @@ export function solveBezierProgressForTime({
const leftHandle =
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 estimate = getBezierPoint({
progress: mid,

View File

@ -13,6 +13,7 @@ import type {
Vector2AnimationBinding,
VectorValue,
} from "@/lib/animation/types";
import { clamp } from "@/utils/math";
interface LinearRgba {
r: number;
@ -25,10 +26,6 @@ export type AnimationComponentValue = number | DiscreteValue;
const toRgb = converter("rgb");
function clamp01({ value }: { value: number }): number {
return Math.max(0, Math.min(1, value));
}
function srgbToLinear({ value }: { value: number }): number {
return value <= 0.04045
? value / 12.92
@ -36,7 +33,7 @@ function srgbToLinear({ 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
? clamped * 12.92
: 1.055 * Math.pow(clamped, 1 / 2.4) - 0.055;
@ -234,7 +231,7 @@ export function parseColorToLinearRgba({
r: srgbToLinear({ value: rgb.r ?? 0 }),
g: srgbToLinear({ value: rgb.g ?? 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 }),
g: linearToSrgb({ value: color.g }),
b: linearToSrgb({ value: color.b }),
alpha: clamp01({ value: color.a }),
alpha: clamp({ value: color.a, min: 0, max: 1 }),
} as const;
return rgb.alpha < 1 ? formatHex8(rgb) : formatHex(rgb);
}

View File

@ -9,6 +9,7 @@ import type {
ScalarSegmentType,
} from "@/lib/animation/types";
import { TIME_EPSILON_SECONDS } from "@/constants/animation-constants";
import { clamp } from "@/utils/math";
import {
getBezierPoint,
getDefaultLeftHandle,
@ -41,10 +42,6 @@ function isWithinTimePair({
);
}
function clamp01({ value }: { value: number }): number {
return Math.max(0, Math.min(1, value));
}
function lerpNumber({
leftValue,
rightValue,
@ -287,8 +284,10 @@ export function getScalarChannelValueAtTime({
return rightKey.value;
}
const progress = clamp01({
const progress = clamp({
value: (time - leftKey.time) / span,
min: 0,
max: 1,
});
if (leftKey.segmentToNext === "linear") {
return lerpNumber({

View File

@ -440,9 +440,9 @@ export function upsertPathKeyframe({
keyframeId?: string;
kind: AnimationBindingKind;
defaultInterpolation: AnimationInterpolation;
coerceValue: (value: AnimationValue) => AnimationValue | null;
coerceValue: ({ value }: { value: AnimationValue }) => AnimationValue | null;
}): ElementAnimations | undefined {
const coercedValue = coerceValue(value);
const coercedValue = coerceValue({ value });
if (coercedValue === null) {
return animations;
}
@ -547,7 +547,7 @@ export function upsertElementKeyframe({
keyframeId,
kind: propertyDefinition.kind,
defaultInterpolation: propertyDefinition.defaultInterpolation,
coerceValue: (nextValue) =>
coerceValue: ({ value: nextValue }) =>
coerceAnimationValueForProperty({
propertyPath,
value: nextValue,

View File

@ -32,9 +32,9 @@ export interface AnimationPathDescriptor {
kind: AnimationBindingKind;
defaultInterpolation: AnimationInterpolation;
numericRanges?: Partial<Record<string, NumericSpec>>;
coerceValue(value: AnimationValue): AnimationValue | null;
getBaseValue(): AnimationValue | null;
setBaseValue(value: AnimationValue): TimelineElement;
coerceValue: ({ value }: { value: AnimationValue }) => AnimationValue | null;
getBaseValue: () => AnimationValue | null;
setBaseValue: ({ value }: { value: AnimationValue }) => TimelineElement;
}
export function getParamValueKind({
@ -135,9 +135,9 @@ function buildGraphicParamDescriptor({
kind: getParamValueKind({ param }),
defaultInterpolation: getParamDefaultInterpolation({ param }),
numericRanges: getParamNumericRange({ param }),
coerceValue: (value) => coerceAnimationValueForParam({ param, value }),
coerceValue: ({ value }) => coerceAnimationValueForParam({ param, value }),
getBaseValue: () => element.params[param.key] ?? param.default,
setBaseValue: (value) => {
setBaseValue: ({ value }) => {
const coercedValue = coerceAnimationValueForParam({ param, value });
if (coercedValue === null) {
return element;
@ -183,9 +183,9 @@ function buildEffectParamDescriptor({
kind: getParamValueKind({ param }),
defaultInterpolation: getParamDefaultInterpolation({ param }),
numericRanges: getParamNumericRange({ param }),
coerceValue: (value) => coerceAnimationValueForParam({ param, value }),
coerceValue: ({ value }) => coerceAnimationValueForParam({ param, value }),
getBaseValue: () => effect.params[param.key] ?? param.default,
setBaseValue: (value) => {
setBaseValue: ({ value }) => {
const coercedValue = coerceAnimationValueForParam({ param, value });
if (coercedValue === null) {
return element;
@ -239,7 +239,7 @@ export function resolveAnimationTarget({
kind: propertyDefinition.kind,
defaultInterpolation: propertyDefinition.defaultInterpolation,
numericRanges: propertyDefinition.numericRanges,
coerceValue: (value) =>
coerceValue: ({ value }) =>
coerceAnimationValueForProperty({
propertyPath: path,
value,
@ -249,7 +249,7 @@ export function resolveAnimationTarget({
element,
propertyPath: path,
}),
setBaseValue: (value) => {
setBaseValue: ({ value }) => {
const coercedValue = propertyDefinition.coerceValue({ value });
if (coercedValue === null) {
return element;

View File

@ -79,7 +79,7 @@ function removeKeyframeAndPersist({
const shouldPersistToBase = isChannelNowEmpty && valueBefore !== null;
const baseElement = shouldPersistToBase
? target.setBaseValue(valueBefore)
? target.setBaseValue({ value: valueBefore })
: element;
return { ...baseElement, animations: nextAnimations };

View File

@ -1,10 +1,44 @@
import { parseColorToLinearRgba } from "@/lib/animation/binding-values";
import { converter, parse } from "culori";
import type { MigrationResult, ProjectRecord } from "./types";
import { getProjectId, isRecord } from "./utils";
const COLOR_COMPONENT_KEYS = ["r", "g", "b", "a"] as const;
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 {
id: string;
time: number;