fix: clean up graph editor

This commit is contained in:
Maze Winther
2026-04-06 13:15:50 +02:00
parent c411edb7c1
commit 79df736431
3 changed files with 66 additions and 52 deletions
@@ -62,9 +62,9 @@ function curvePath({ curve }: { curve: NormalizedCubicBezier }) {
const points: string[] = []; const points: string[] = [];
for (let i = 0; i <= CURVE_SEGMENTS; i++) { for (let i = 0; i <= CURVE_SEGMENTS; i++) {
const progress = i / CURVE_SEGMENTS; const progress = i / CURVE_SEGMENTS;
points.push( const x = toSvgX({ value: getBezierPoint({ progress, p0: 0, p1: curve[0], p2: curve[2], p3: 1 }) });
`${toSvgX({ value: getBezierPoint({ progress, p0: 0, p1: curve[0], p2: curve[2], p3: 1 }) })},${toSvgY({ value: getBezierPoint({ progress, p0: 0, p1: curve[1], p2: curve[3], p3: 1 }) })}`, const y = toSvgY({ value: getBezierPoint({ progress, p0: 0, p1: curve[1], p2: curve[3], p3: 1 }) });
); points.push(`${x},${y}`);
} }
return `M${points.join("L")}`; return `M${points.join("L")}`;
} }
@@ -299,13 +299,21 @@ function PresetItem({
); );
} }
function toThumbX({ value }: { value: number }) {
return THUMB_PADDING_X + value * (THUMB_WIDTH - THUMB_PADDING_X * 2);
}
function toThumbY({ value }: { value: number }) {
return THUMB_PADDING_Y + (1 - value) * (THUMB_HEIGHT - THUMB_PADDING_Y * 2);
}
function CurveThumb({ value }: { value: NormalizedCubicBezier }) { function CurveThumb({ value }: { value: NormalizedCubicBezier }) {
const points: string[] = []; const points: string[] = [];
for (let i = 0; i <= THUMB_SEGMENTS; i++) { for (let i = 0; i <= THUMB_SEGMENTS; i++) {
const progress = i / THUMB_SEGMENTS; const progress = i / THUMB_SEGMENTS;
points.push( const x = toThumbX({ value: getBezierPoint({ progress, p0: 0, p1: value[0], p2: value[2], p3: 1 }) });
`${THUMB_PADDING_X + getBezierPoint({ progress, p0: 0, p1: value[0], p2: value[2], p3: 1 }) * (THUMB_WIDTH - THUMB_PADDING_X * 2)},${THUMB_PADDING_Y + (1 - getBezierPoint({ progress, p0: 0, p1: value[1], p2: value[3], p3: 1 })) * (THUMB_HEIGHT - THUMB_PADDING_Y * 2)}`, const y = toThumbY({ value: getBezierPoint({ progress, p0: 0, p1: value[1], p2: value[3], p3: 1 }) });
); points.push(`${x},${y}`);
} }
return ( return (
<svg <svg
@@ -89,55 +89,61 @@ export function useGraphEditorController() {
[discardPreview], [discardPreview],
); );
function handlePreviewValue(nextValue: NormalizedCubicBezier) { const handlePreviewValue = useCallback(
if (state.status !== "ready") { (nextValue: NormalizedCubicBezier) => {
return; if (state.status !== "ready") {
} return;
}
const nextAnimations = applyGraphEditorCurvePreview({ const nextAnimations = applyGraphEditorCurvePreview({
animations: state.element.animations, animations: state.element.animations,
context: state.context, context: state.context,
cubicBezier: nextValue, cubicBezier: nextValue,
}); });
editor.timeline.previewElements({ editor.timeline.previewElements({
updates: [ updates: [
{ {
trackId: state.trackId,
elementId: state.elementId,
updates: {
animations: nextAnimations,
},
},
],
});
hasPreviewRef.current = true;
},
[editor, state],
);
const handleCommitValue = useCallback(
(nextValue: NormalizedCubicBezier) => {
if (state.status !== "ready") {
return;
}
const patches = buildGraphEditorCurvePatches({
context: state.context,
cubicBezier: nextValue,
});
if (!patches) {
return;
}
editor.timeline.updateKeyframeCurves({
keyframes: patches.map(({ keyframeId, patch }) => ({
trackId: state.trackId, trackId: state.trackId,
elementId: state.elementId, elementId: state.elementId,
updates: { propertyPath: state.propertyPath,
animations: nextAnimations, componentKey: state.context.componentKey,
}, keyframeId,
}, patch,
], })),
}); });
hasPreviewRef.current = true; hasPreviewRef.current = false;
} },
[editor, state],
function handleCommitValue(nextValue: NormalizedCubicBezier) { );
if (state.status !== "ready") {
return;
}
const patches = buildGraphEditorCurvePatches({
context: state.context,
cubicBezier: nextValue,
});
if (!patches) {
return;
}
editor.timeline.updateKeyframeCurves({
keyframes: patches.map(({ keyframeId, patch }) => ({
trackId: state.trackId,
elementId: state.elementId,
propertyPath: state.propertyPath,
componentKey: state.context.componentKey,
keyframeId,
patch,
})),
});
hasPreviewRef.current = false;
}
return { return {
open, open,