diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-13 04:41:33 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-13 05:08:29 +0200 |
| commit | 7d4e166678c35d48b22b8d1d05639f981a956a8b (patch) | |
| tree | eb596d4f4609053524224169c247db24052db353 /apps/web/src/lib/text/layout.ts | |
| parent | cad88ee4d1c9e06e7529b88ef5e5d3fdf3f89149 (diff) | |
| download | opencut-7d4e166678c35d48b22b8d1d05639f981a956a8b.tar.gz | |
fix: text measurement, rotation handle, video cache races, playback sync
Made-with: Cursor
Diffstat (limited to 'apps/web/src/lib/text/layout.ts')
| -rw-r--r-- | apps/web/src/lib/text/layout.ts | 51 |
1 files changed, 49 insertions, 2 deletions
diff --git a/apps/web/src/lib/text/layout.ts b/apps/web/src/lib/text/layout.ts index 75fa7f09..a3badfdc 100644 --- a/apps/web/src/lib/text/layout.ts +++ b/apps/web/src/lib/text/layout.ts @@ -14,15 +14,18 @@ export interface TextBlockMeasurement { maxWidth: number; } -type CanvasContext = +export type TextCanvasContext = | CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D; +const TEXT_DECORATION_THICKNESS_RATIO = 0.07; +const STRIKETHROUGH_VERTICAL_RATIO = 0.35; + export function setCanvasLetterSpacing({ ctx, letterSpacingPx, }: { - ctx: CanvasContext; + ctx: TextCanvasContext; letterSpacingPx: number; }): void { if ("letterSpacing" in ctx) { @@ -177,3 +180,47 @@ export function getTextVisualRect({ height: bottom - top, }; } + +export function drawTextDecoration({ + ctx, + textDecoration, + lineWidth, + lineY, + metrics, + scaledFontSize, + textAlign, +}: { + ctx: TextCanvasContext; + textDecoration: string; + lineWidth: number; + lineY: number; + metrics: TextMetrics; + scaledFontSize: number; + textAlign: CanvasTextAlign; +}): void { + if (textDecoration === "none" || !textDecoration) return; + + const thickness = Math.max( + 1, + scaledFontSize * TEXT_DECORATION_THICKNESS_RATIO, + ); + const ascent = getMetricAscent({ metrics, fallbackFontSize: scaledFontSize }); + const descent = getMetricDescent({ + metrics, + fallbackFontSize: scaledFontSize, + }); + + let xStart = -lineWidth / 2; + if (textAlign === "left") xStart = 0; + if (textAlign === "right") xStart = -lineWidth; + + if (textDecoration === "underline") { + const underlineY = lineY + descent + thickness; + ctx.fillRect(xStart, underlineY, lineWidth, thickness); + } + + if (textDecoration === "line-through") { + const strikeY = lineY - (ascent - descent) * STRIKETHROUGH_VERTICAL_RATIO; + ctx.fillRect(xStart, strikeY, lineWidth, thickness); + } +} |
