diff options
| author | Maze <167211895+mazeincoding@users.noreply.github.com> | 2026-01-31 00:20:04 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-31 00:20:04 +0100 |
| commit | 7bf0984698e43b9aa2dd879f65951318ed410390 (patch) | |
| tree | ccf2cd590a47d550152afc07129cb55c71a9ec35 /apps/web/src/services/renderer/nodes/text-node.ts | |
| parent | 0173db99448d1c4c6415dcf15272e17551b48b04 (diff) | |
| download | opencut-7bf0984698e43b9aa2dd879f65951318ed410390.tar.gz | |
codebase overhaul (#697)
Diffstat (limited to 'apps/web/src/services/renderer/nodes/text-node.ts')
| -rw-r--r-- | apps/web/src/services/renderer/nodes/text-node.ts | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/apps/web/src/services/renderer/nodes/text-node.ts b/apps/web/src/services/renderer/nodes/text-node.ts new file mode 100644 index 00000000..2cbf3d2a --- /dev/null +++ b/apps/web/src/services/renderer/nodes/text-node.ts @@ -0,0 +1,74 @@ +import type { CanvasRenderer } from "../canvas-renderer"; +import { BaseNode } from "./base-node"; +import type { TextElement } from "@/types/timeline"; + +export type TextNodeParams = TextElement & { + canvasCenter: { x: number; y: number }; + textBaseline?: CanvasTextBaseline; +}; + +export class TextNode extends BaseNode<TextNodeParams> { + isInRange({ time }: { time: number }) { + return ( + time >= this.params.startTime && + time < this.params.startTime + this.params.duration + ); + } + + async render({ renderer, time }: { renderer: CanvasRenderer; time: number }) { + if (!this.isInRange({ time })) { + return; + } + + renderer.context.save(); + + const x = this.params.transform.position.x + this.params.canvasCenter.x; + const y = this.params.transform.position.y + this.params.canvasCenter.y; + + renderer.context.translate(x, y); + if (this.params.transform.rotate) { + renderer.context.rotate((this.params.transform.rotate * Math.PI) / 180); + } + + 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}`; + renderer.context.textAlign = this.params.textAlign; + renderer.context.textBaseline = this.params.textBaseline || "middle"; + renderer.context.fillStyle = this.params.color; + + const prevAlpha = renderer.context.globalAlpha; + renderer.context.globalAlpha = this.params.opacity; + + if (this.params.backgroundColor) { + const metrics = renderer.context.measureText(this.params.content); + const ascent = + metrics.actualBoundingBoxAscent ?? this.params.fontSize * 0.8; + const descent = + metrics.actualBoundingBoxDescent ?? this.params.fontSize * 0.2; + const textW = metrics.width; + const textH = ascent + descent; + const padX = 8; + const padY = 4; + + renderer.context.fillStyle = this.params.backgroundColor; + let bgLeft = -textW / 2; + if (renderer.context.textAlign === "left") bgLeft = 0; + if (renderer.context.textAlign === "right") bgLeft = -textW; + + renderer.context.fillRect( + bgLeft - padX, + -textH / 2 - padY, + textW + padX * 2, + textH + padY * 2, + ); + + renderer.context.fillStyle = this.params.color; + } + + renderer.context.fillText(this.params.content, 0, 0); + + renderer.context.globalAlpha = prevAlpha; + renderer.context.restore(); + } +} |
