From c6666fd385588f11d6ec887455a115d525e2e6c2 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Sun, 8 Feb 2026 16:50:31 +0100 Subject: [PATCH] feat: update text properties to use dynamic font size limits and scaling --- .../panels/properties/text-properties.tsx | 13 ++++++----- apps/web/src/constants/text-constants.ts | 11 +++++++++- .../src/services/renderer/nodes/text-node.ts | 22 ++++++++++++++++--- .../src/services/renderer/scene-builder.ts | 1 + 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/apps/web/src/components/editor/panels/properties/text-properties.tsx b/apps/web/src/components/editor/panels/properties/text-properties.tsx index f8c9bf87..8f40910b 100644 --- a/apps/web/src/components/editor/panels/properties/text-properties.tsx +++ b/apps/web/src/components/editor/panels/properties/text-properties.tsx @@ -30,6 +30,7 @@ import { } from "@/components/ui/tooltip"; import { useEditor } from "@/hooks/use-editor"; import { DEFAULT_COLOR } from "@/constants/project-constants"; +import { MIN_FONT_SIZE, MAX_FONT_SIZE } from "@/constants/text-constants"; export function TextProperties({ element, @@ -57,7 +58,7 @@ export function TextProperties({ const parsed = parseInt(value, 10); const fontSize = Number.isNaN(parsed) ? element.fontSize - : clamp({ value: parsed, min: 8, max: 300 }); + : clamp({ value: parsed, min: MIN_FONT_SIZE, max: MAX_FONT_SIZE }); editor.timeline.updateTextElement({ trackId, elementId: element.id, @@ -70,7 +71,7 @@ export function TextProperties({ const parsed = parseInt(fontSizeInput, 10); const fontSize = Number.isNaN(parsed) ? element.fontSize - : clamp({ value: parsed, min: 8, max: 300 }); + : clamp({ value: parsed, min: MIN_FONT_SIZE, max: MAX_FONT_SIZE }); setFontSizeInput(fontSize.toString()); editor.timeline.updateTextElement({ trackId, @@ -274,8 +275,8 @@ export function TextProperties({
{ editor.timeline.updateTextElement({ @@ -290,8 +291,8 @@ export function TextProperties({ handleFontSizeChange({ value: e.target.value }) } diff --git a/apps/web/src/constants/text-constants.ts b/apps/web/src/constants/text-constants.ts index c8a23655..932d494b 100644 --- a/apps/web/src/constants/text-constants.ts +++ b/apps/web/src/constants/text-constants.ts @@ -1,11 +1,20 @@ import type { TextElement } from "@/types/timeline"; import { TIMELINE_CONSTANTS } from "./timeline-constants"; +export const MIN_FONT_SIZE = 5; +export const MAX_FONT_SIZE = 300; + +/** + * higher value: smaller font size + * lower value: larger font size + */ +export const FONT_SIZE_SCALE_REFERENCE = 90; + export const DEFAULT_TEXT_ELEMENT: Omit = { type: "text", name: "Text", content: "Default Text", - fontSize: 48, + fontSize: 15, fontFamily: "Arial", color: "#ffffff", backgroundColor: "transparent", diff --git a/apps/web/src/services/renderer/nodes/text-node.ts b/apps/web/src/services/renderer/nodes/text-node.ts index 2cbf3d2a..bb79e086 100644 --- a/apps/web/src/services/renderer/nodes/text-node.ts +++ b/apps/web/src/services/renderer/nodes/text-node.ts @@ -1,9 +1,21 @@ import type { CanvasRenderer } from "../canvas-renderer"; import { BaseNode } from "./base-node"; import type { TextElement } from "@/types/timeline"; +import { FONT_SIZE_SCALE_REFERENCE } from "@/constants/text-constants"; + +function scaleFontSize({ + fontSize, + canvasHeight, +}: { + fontSize: number; + canvasHeight: number; +}): number { + return fontSize * (canvasHeight / FONT_SIZE_SCALE_REFERENCE); +} export type TextNodeParams = TextElement & { canvasCenter: { x: number; y: number }; + canvasHeight: number; textBaseline?: CanvasTextBaseline; }; @@ -32,7 +44,11 @@ export class TextNode extends BaseNode { const fontWeight = this.params.fontWeight === "bold" ? "bold" : "normal"; const fontStyle = this.params.fontStyle === "italic" ? "italic" : "normal"; - renderer.context.font = `${fontStyle} ${fontWeight} ${this.params.fontSize}px ${this.params.fontFamily}`; + const scaledFontSize = scaleFontSize({ + fontSize: this.params.fontSize, + canvasHeight: this.params.canvasHeight, + }); + renderer.context.font = `${fontStyle} ${fontWeight} ${scaledFontSize}px ${this.params.fontFamily}`; renderer.context.textAlign = this.params.textAlign; renderer.context.textBaseline = this.params.textBaseline || "middle"; renderer.context.fillStyle = this.params.color; @@ -43,9 +59,9 @@ export class TextNode extends BaseNode { if (this.params.backgroundColor) { const metrics = renderer.context.measureText(this.params.content); const ascent = - metrics.actualBoundingBoxAscent ?? this.params.fontSize * 0.8; + metrics.actualBoundingBoxAscent ?? scaledFontSize * 0.8; const descent = - metrics.actualBoundingBoxDescent ?? this.params.fontSize * 0.2; + metrics.actualBoundingBoxDescent ?? scaledFontSize * 0.2; const textW = metrics.width; const textH = ascent + descent; const padX = 8; diff --git a/apps/web/src/services/renderer/scene-builder.ts b/apps/web/src/services/renderer/scene-builder.ts index d5b189cd..5d33524e 100644 --- a/apps/web/src/services/renderer/scene-builder.ts +++ b/apps/web/src/services/renderer/scene-builder.ts @@ -85,6 +85,7 @@ export function buildScene(params: BuildSceneParams) { new TextNode({ ...element, canvasCenter: { x: canvasSize.width / 2, y: canvasSize.height / 2 }, + canvasHeight: canvasSize.height, textBaseline: "middle", }), );