UNPKG

cheetah-grid

Version:

Cheetah Grid is a high performance grid engine that works on canvas

161 lines (157 loc) 4.18 kB
import type { AnyFunction, ColorDef, InlineAPI, RectProps } from "../ts-types"; import type { Canvashelper } from "../tools/canvashelper"; import type { GenWordsResult } from "../internal/utils"; import { str } from "../internal/utils"; function getWidth(ctx: CanvasRenderingContext2D, content: string): number { return ctx.measureText(content).width; } function breakWidth( ctx: CanvasRenderingContext2D, content: string, itr: GenWordsResult, candidateIndex: number, width: number ): { before: Inline | null; after: Inline | null; } { const chars = []; let ret = itr.next(); for (let i = 0; i < candidateIndex && ret !== null; i++, ret = itr.next()) { chars.push(ret); } let beforeWidth = getWidth(ctx, chars.join("")); if (beforeWidth > width) { while (chars.length) { const c = chars.pop(); beforeWidth -= getWidth(ctx, c || ""); if (beforeWidth <= width) { break; } } } else if (beforeWidth < width) { while (ret !== null) { const charWidth = getWidth(ctx, ret); if (beforeWidth + charWidth > width) { break; } chars.push(ret); beforeWidth += charWidth; ret = itr.next(); } } const beforeContent = chars.join("").replace(/\s+$/, ""); const afterContent = content.slice(beforeContent.length).replace(/^\s+/, ""); return { before: beforeContent ? new Inline(beforeContent) : null, after: afterContent ? new Inline(afterContent) : null, }; } export type InlineDrawOption = { ctx: CanvasRenderingContext2D; canvashelper: Canvashelper; rect: RectProps; offset: number; offsetLeft: number; offsetRight: number; offsetTop: number; offsetBottom: number; }; export class Inline implements InlineAPI { private _content: string; constructor(content?: string) { this._content = content != null ? content : ""; } width({ ctx }: { ctx: CanvasRenderingContext2D }): number { return getWidth(ctx, this._content); } font(): string | null { return null; } color(): ColorDef | null { return null; } canDraw(): boolean { return true; } // eslint-disable-next-line @typescript-eslint/no-empty-function onReady(_callback: AnyFunction): void {} draw({ ctx, canvashelper, rect, offset, offsetLeft, offsetRight, offsetTop, offsetBottom, }: InlineDrawOption): void { canvashelper.fillTextRect( ctx, this._content, rect.left, rect.top, rect.width, rect.height, { offset: offset + 1, padding: { left: offsetLeft, right: offsetRight, top: offsetTop, bottom: offsetBottom, }, } ); } canBreak(): boolean { return !!this._content; } splitIndex(index: number): { before: Inline | null; after: Inline | null; } { const content = this._content; const itr = str.genChars(content); const chars = []; let ret = itr.next(); for (let i = 0; i < index && ret !== null; i++, ret = itr.next()) { chars.push(ret); } const beforeContent = chars.join(""); const afterContent = content.slice(beforeContent.length); return { before: beforeContent ? new Inline(beforeContent) : null, after: afterContent ? new Inline(afterContent) : null, }; } breakWord( ctx: CanvasRenderingContext2D, width: number ): { before: Inline | null; after: Inline | null; } { const content = this._content; const allWidth = this.width({ ctx }); const candidate = Math.floor((this._content.length * width) / allWidth); const itr = str.genWords(content); return breakWidth(ctx, content, itr, candidate, width); } breakAll( ctx: CanvasRenderingContext2D, width: number ): { before: Inline | null; after: Inline | null; } { const content = this._content; const allWidth = this.width({ ctx }); const candidate = Math.floor((this._content.length * width) / allWidth); const itr = str.genChars(content); return breakWidth(ctx, content, itr, candidate, width); } toString(): string { return this._content; } }