summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-06 13:15:50 +0200
committerMaze Winther <mazewinther@gmail.com>2026-04-06 13:15:50 +0200
commit79df73643185c02493364a077402726d1fa7b9ec (patch)
treeb761c647c002d1112e65de474ae593257169d11b
parentc411edb7c160c55e94fddf62fd60fa1e166351c0 (diff)
downloadopencut-79df73643185c02493364a077402726d1fa7b9ec.tar.gz
fix: clean up graph editor
-rw-r--r--apps/web/src/components/editor/panels/timeline/graph-editor/bezier-graph.tsx6
-rw-r--r--apps/web/src/components/editor/panels/timeline/graph-editor/popover.tsx14
-rw-r--r--apps/web/src/components/editor/panels/timeline/graph-editor/use-controller.ts94
3 files changed, 64 insertions, 50 deletions
diff --git a/apps/web/src/components/editor/panels/timeline/graph-editor/bezier-graph.tsx b/apps/web/src/components/editor/panels/timeline/graph-editor/bezier-graph.tsx
index f607dc7a..3f78350b 100644
--- a/apps/web/src/components/editor/panels/timeline/graph-editor/bezier-graph.tsx
+++ b/apps/web/src/components/editor/panels/timeline/graph-editor/bezier-graph.tsx
@@ -62,9 +62,9 @@ function curvePath({ curve }: { curve: NormalizedCubicBezier }) {
const points: string[] = [];
for (let i = 0; i <= CURVE_SEGMENTS; i++) {
const progress = i / CURVE_SEGMENTS;
- points.push(
- `${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 x = toSvgX({ value: getBezierPoint({ progress, p0: 0, p1: curve[0], p2: curve[2], 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")}`;
}
diff --git a/apps/web/src/components/editor/panels/timeline/graph-editor/popover.tsx b/apps/web/src/components/editor/panels/timeline/graph-editor/popover.tsx
index 00348a60..733195ed 100644
--- a/apps/web/src/components/editor/panels/timeline/graph-editor/popover.tsx
+++ b/apps/web/src/components/editor/panels/timeline/graph-editor/popover.tsx
@@ -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 }) {
const points: string[] = [];
for (let i = 0; i <= THUMB_SEGMENTS; i++) {
const progress = i / THUMB_SEGMENTS;
- points.push(
- `${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 x = toThumbX({ value: getBezierPoint({ progress, p0: 0, p1: value[0], p2: value[2], p3: 1 }) });
+ const y = toThumbY({ value: getBezierPoint({ progress, p0: 0, p1: value[1], p2: value[3], p3: 1 }) });
+ points.push(`${x},${y}`);
}
return (
<svg
diff --git a/apps/web/src/components/editor/panels/timeline/graph-editor/use-controller.ts b/apps/web/src/components/editor/panels/timeline/graph-editor/use-controller.ts
index 3b7520e6..41df702f 100644
--- a/apps/web/src/components/editor/panels/timeline/graph-editor/use-controller.ts
+++ b/apps/web/src/components/editor/panels/timeline/graph-editor/use-controller.ts
@@ -89,55 +89,61 @@ export function useGraphEditorController() {
[discardPreview],
);
- function handlePreviewValue(nextValue: NormalizedCubicBezier) {
- if (state.status !== "ready") {
- return;
- }
+ const handlePreviewValue = useCallback(
+ (nextValue: NormalizedCubicBezier) => {
+ if (state.status !== "ready") {
+ return;
+ }
- const nextAnimations = applyGraphEditorCurvePreview({
- animations: state.element.animations,
- context: state.context,
- cubicBezier: nextValue,
- });
- editor.timeline.previewElements({
- updates: [
- {
- trackId: state.trackId,
- elementId: state.elementId,
- updates: {
- animations: nextAnimations,
+ const nextAnimations = applyGraphEditorCurvePreview({
+ animations: state.element.animations,
+ context: state.context,
+ cubicBezier: nextValue,
+ });
+ editor.timeline.previewElements({
+ updates: [
+ {
+ trackId: state.trackId,
+ elementId: state.elementId,
+ updates: {
+ animations: nextAnimations,
+ },
},
- },
- ],
- });
- hasPreviewRef.current = true;
- }
+ ],
+ });
+ hasPreviewRef.current = true;
+ },
+ [editor, state],
+ );
- function handleCommitValue(nextValue: NormalizedCubicBezier) {
- if (state.status !== "ready") {
- return;
- }
+ const handleCommitValue = useCallback(
+ (nextValue: NormalizedCubicBezier) => {
+ if (state.status !== "ready") {
+ return;
+ }
- const patches = buildGraphEditorCurvePatches({
- context: state.context,
- cubicBezier: nextValue,
- });
- if (!patches) {
- 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;
- }
+ 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;
+ },
+ [editor, state],
+ );
return {
open,