From 2ee0a44735d2b930348e9774909638b2c1909738 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Wed, 29 Apr 2026 01:28:50 +0200 Subject: refactor: unify element params --- .../src/preview/components/text-edit-overlay.tsx | 45 ++++++----- .../controllers/preview-interaction-controller.ts | 19 ++--- .../controllers/transform-handle-controller.ts | 87 +++++++++++++++------- apps/web/src/preview/element-bounds.ts | 9 ++- 4 files changed, 98 insertions(+), 62 deletions(-) (limited to 'apps/web/src/preview') diff --git a/apps/web/src/preview/components/text-edit-overlay.tsx b/apps/web/src/preview/components/text-edit-overlay.tsx index dd6364d6..c06d50ed 100644 --- a/apps/web/src/preview/components/text-edit-overlay.tsx +++ b/apps/web/src/preview/components/text-edit-overlay.tsx @@ -9,7 +9,12 @@ import { getElementLocalTime, } from "@/animation"; import { resolveTransformAtTime } from "@/rendering/animation-values"; +import { buildTransformFromParams } from "@/rendering"; import { resolveTextLayout } from "@/text/primitives"; +import { + buildTextBackgroundFromElement, + buildTextLayoutParamsFromElement, +} from "@/text/measure-element"; export function TextEditOverlay({ trackId, @@ -42,7 +47,7 @@ export function TextEditOverlay({ if (!div) return; const text = div.innerText; editor.timeline.previewElements({ - updates: [{ trackId, elementId, updates: { content: text } }], + updates: [{ trackId, elementId, updates: { params: { content: text } } }], }); }, [editor.timeline, trackId, elementId]); @@ -69,7 +74,7 @@ export function TextEditOverlay({ elementDuration: element.duration, }); const transform = resolveTransformAtTime({ - baseTransform: element.transform, + baseTransform: buildTransformFromParams({ params: element.params }), animations: element.animations, localTime, }); @@ -80,26 +85,17 @@ export function TextEditOverlay({ }); const { x: displayScaleX } = viewport.getDisplayScale(); + const textParams = buildTextLayoutParamsFromElement({ element }); const resolvedTextLayout = resolveTextLayout({ - text: { - content: element.content, - fontSize: element.fontSize, - fontFamily: element.fontFamily, - fontWeight: element.fontWeight, - fontStyle: element.fontStyle, - textAlign: element.textAlign, - textDecoration: element.textDecoration, - letterSpacing: element.letterSpacing, - lineHeight: element.lineHeight, - }, + text: textParams, canvasHeight: canvasSize.height, }); - const lineHeight = element.lineHeight ?? DEFAULTS.text.lineHeight; - const canvasLetterSpacing = element.letterSpacing ?? 0; + const lineHeight = textParams.lineHeight ?? DEFAULTS.text.lineHeight; + const canvasLetterSpacing = textParams.letterSpacing ?? 0; const lineHeightPx = resolvedTextLayout.lineHeightPx; - const bg = element.background; + const bg = buildTextBackgroundFromElement({ element }); const shouldShowBackground = bg.enabled && bg.color && bg.color !== "transparent"; const fontSizeRatio = resolvedTextLayout.fontSizeRatio; @@ -130,17 +126,20 @@ export function TextEditOverlay({ className="cursor-text select-text outline-none whitespace-pre" style={{ fontSize: resolvedTextLayout.scaledFontSize, - fontFamily: element.fontFamily, - fontWeight: element.fontWeight === "bold" ? "bold" : "normal", - fontStyle: element.fontStyle === "italic" ? "italic" : "normal", - textAlign: element.textAlign, + fontFamily: textParams.fontFamily, + fontWeight: textParams.fontWeight === "bold" ? "bold" : "normal", + fontStyle: textParams.fontStyle === "italic" ? "italic" : "normal", + textAlign: textParams.textAlign, letterSpacing: `${canvasLetterSpacing}px`, lineHeight, color: "transparent", - caretColor: element.color, + caretColor: + typeof element.params.color === "string" + ? element.params.color + : "#ffffff", backgroundColor: shouldShowBackground ? bg.color : "transparent", minHeight: lineHeightPx, - textDecoration: element.textDecoration ?? "none", + textDecoration: textParams.textDecoration ?? "none", padding: shouldShowBackground ? `${canvasPaddingY}px ${canvasPaddingX}px` : 0, @@ -150,7 +149,7 @@ export function TextEditOverlay({ onBlur={onCommit} onKeyDown={(event) => handleKeyDown({ event })} > - {element.content || ""} + {textParams.content} ); diff --git a/apps/web/src/preview/controllers/preview-interaction-controller.ts b/apps/web/src/preview/controllers/preview-interaction-controller.ts index 00573662..118b48ae 100644 --- a/apps/web/src/preview/controllers/preview-interaction-controller.ts +++ b/apps/web/src/preview/controllers/preview-interaction-controller.ts @@ -18,7 +18,8 @@ import { type SnapLine, } from "@/preview/preview-snap"; import type { TCanvasSize } from "@/project/types"; -import type { Transform } from "@/rendering"; +import type { ParamValues } from "@/params"; +import { buildTransformFromParams, type Transform } from "@/rendering"; import { isVisualElement } from "@/timeline/element-utils"; import type { ElementRef, @@ -51,6 +52,7 @@ interface DragElementSnapshot { readonly trackId: string; readonly elementId: string; readonly initialTransform: Transform; + readonly initialParams: ParamValues; } interface DraggingGesture extends CapturedPointerState { @@ -218,7 +220,8 @@ function toDragElementSnapshots({ .map(({ track, element }) => ({ trackId: track.id, elementId: element.id, - initialTransform: element.transform, + initialTransform: buildTransformFromParams({ params: element.params }), + initialParams: element.params, })); } @@ -565,16 +568,14 @@ export class PreviewInteractionController { snappedPosition.y - firstElement.initialTransform.position.y; this.deps.timeline.previewElements( - drag.elements.map(({ trackId, elementId, initialTransform }) => ({ + drag.elements.map(({ trackId, elementId, initialTransform, initialParams }) => ({ trackId, elementId, updates: { - transform: { - ...initialTransform, - position: { - x: initialTransform.position.x + deltaSnappedX, - y: initialTransform.position.y + deltaSnappedY, - }, + params: { + ...initialParams, + "transform.positionX": initialTransform.position.x + deltaSnappedX, + "transform.positionY": initialTransform.position.y + deltaSnappedY, }, }, })), diff --git a/apps/web/src/preview/controllers/transform-handle-controller.ts b/apps/web/src/preview/controllers/transform-handle-controller.ts index 67f677f6..d9b5ef1f 100644 --- a/apps/web/src/preview/controllers/transform-handle-controller.ts +++ b/apps/web/src/preview/controllers/transform-handle-controller.ts @@ -23,7 +23,8 @@ import { setChannel, } from "@/animation"; import type { ElementAnimations } from "@/animation/types"; -import type { Transform } from "@/rendering"; +import type { ParamValues } from "@/params"; +import { buildTransformFromParams, type Transform } from "@/rendering"; import { resolveTransformAtTime } from "@/rendering/animation-values"; import type { ElementRef, @@ -47,6 +48,7 @@ interface CornerScaleSession extends CapturedPointerState { readonly trackId: string; readonly elementId: string; readonly initialTransform: Transform; + readonly initialParams: ParamValues; readonly initialDistance: number; readonly initialBoundsCx: number; readonly initialBoundsCy: number; @@ -62,6 +64,7 @@ interface EdgeScaleSession extends CapturedPointerState { readonly trackId: string; readonly elementId: string; readonly initialTransform: Transform; + readonly initialParams: ParamValues; readonly initialBoundsCx: number; readonly initialBoundsCy: number; readonly baseWidth: number; @@ -76,6 +79,7 @@ interface RotationSession extends CapturedPointerState { readonly trackId: string; readonly elementId: string; readonly initialTransform: Transform; + readonly initialParams: ParamValues; readonly initialAngle: number; readonly initialBoundsCx: number; readonly initialBoundsCy: number; @@ -369,6 +373,7 @@ export class TransformHandleController { trackId: context.trackId, elementId: context.elementId, initialTransform: context.resolvedTransform, + initialParams: context.element.params, initialDistance: getCornerDistance({ bounds: context.bounds, corner, @@ -410,6 +415,7 @@ export class TransformHandleController { trackId: context.trackId, elementId: context.elementId, initialTransform: context.resolvedTransform, + initialParams: context.element.params, initialAngle, initialBoundsCx: context.bounds.cx, initialBoundsCy: context.bounds.cy, @@ -447,6 +453,7 @@ export class TransformHandleController { trackId: context.trackId, elementId: context.elementId, initialTransform: context.resolvedTransform, + initialParams: context.element.params, initialBoundsCx: context.bounds.cx, initialBoundsCy: context.bounds.cy, baseWidth: context.bounds.width / context.resolvedTransform.scaleX, @@ -561,7 +568,9 @@ export class TransformHandleController { element: selectedWithBounds.element, bounds: selectedWithBounds.bounds, resolvedTransform: resolveTransformAtTime({ - baseTransform: selectedWithBounds.element.transform, + baseTransform: buildTransformFromParams({ + params: selectedWithBounds.element.params, + }), animations: selectedWithBounds.element.animations, localTime, }), @@ -608,15 +617,18 @@ export class TransformHandleController { trackId: session.trackId, elementId: session.elementId, updates: { - transform: { - ...session.initialTransform, - scaleX: clampScaleNonZero( - session.initialTransform.scaleX * snappedScale, - ), - scaleY: clampScaleNonZero( - session.initialTransform.scaleY * snappedScale, - ), - }, + params: buildParamsWithTransform({ + params: session.initialParams, + transform: { + ...session.initialTransform, + scaleX: clampScaleNonZero( + session.initialTransform.scaleX * snappedScale, + ), + scaleY: clampScaleNonZero( + session.initialTransform.scaleY * snappedScale, + ), + }, + }), ...(session.shouldClearScaleAnimation && { animations: session.animationsWithoutScale, }), @@ -699,17 +711,20 @@ export class TransformHandleController { trackId: session.trackId, elementId: session.elementId, updates: { - transform: { - ...session.initialTransform, - scaleX: - session.edge === "right" || session.edge === "left" - ? xSnap.snappedScale - : session.initialTransform.scaleX, - scaleY: - session.edge === "bottom" - ? ySnap.snappedScale - : session.initialTransform.scaleY, - }, + params: buildParamsWithTransform({ + params: session.initialParams, + transform: { + ...session.initialTransform, + scaleX: + session.edge === "right" || session.edge === "left" + ? xSnap.snappedScale + : session.initialTransform.scaleX, + scaleY: + session.edge === "bottom" + ? ySnap.snappedScale + : session.initialTransform.scaleY, + }, + }), ...(session.shouldClearScaleAnimation && { animations: session.animationsWithoutScale, }), @@ -742,12 +757,32 @@ export class TransformHandleController { trackId: session.trackId, elementId: session.elementId, updates: { - transform: { - ...session.initialTransform, - rotate: snappedRotation, - }, + params: buildParamsWithTransform({ + params: session.initialParams, + transform: { + ...session.initialTransform, + rotate: snappedRotation, + }, + }), }, }, ]); } } + +function buildParamsWithTransform({ + params, + transform, +}: { + params: ParamValues; + transform: Transform; +}): ParamValues { + return { + ...params, + "transform.positionX": transform.position.x, + "transform.positionY": transform.position.y, + "transform.scaleX": transform.scaleX, + "transform.scaleY": transform.scaleY, + "transform.rotate": transform.rotate, + }; +} diff --git a/apps/web/src/preview/element-bounds.ts b/apps/web/src/preview/element-bounds.ts index 0afc6e4e..181fdf1f 100644 --- a/apps/web/src/preview/element-bounds.ts +++ b/apps/web/src/preview/element-bounds.ts @@ -7,6 +7,7 @@ import { getElementLocalTime, } from "@/animation"; import { resolveTransformAtTime } from "@/rendering/animation-values"; +import { buildTransformFromParams } from "@/rendering"; export interface ElementBounds { cx: number; @@ -123,7 +124,7 @@ function getElementBounds({ if (element.type === "video" || element.type === "image") { const transform = resolveTransformAtTime({ - baseTransform: element.transform, + baseTransform: buildTransformFromParams({ params: element.params }), animations: element.animations, localTime, }); @@ -140,7 +141,7 @@ function getElementBounds({ if (element.type === "sticker") { const transform = resolveTransformAtTime({ - baseTransform: element.transform, + baseTransform: buildTransformFromParams({ params: element.params }), animations: element.animations, localTime, }); @@ -155,7 +156,7 @@ function getElementBounds({ if (element.type === "graphic") { const transform = resolveTransformAtTime({ - baseTransform: element.transform, + baseTransform: buildTransformFromParams({ params: element.params }), animations: element.animations, localTime, }); @@ -170,7 +171,7 @@ function getElementBounds({ if (element.type === "text") { const transform = resolveTransformAtTime({ - baseTransform: element.transform, + baseTransform: buildTransformFromParams({ params: element.params }), animations: element.animations, localTime, }); -- cgit v1.3.1