feat(createCharacterComparison): add generic for font type and checks for the absence of the fonts

This commit is contained in:
Ilia Mashkov
2026-01-26 12:34:27 +03:00
parent 9b8ebed1c3
commit 2b820230bc

View File

@@ -15,16 +15,22 @@ export interface LineData {
* @param fontB - The second font definition * @param fontB - The second font definition
* @returns Object with reactive state (lines, containerWidth) and methods (breakIntoLines, getCharState) * @returns Object with reactive state (lines, containerWidth) and methods (breakIntoLines, getCharState)
*/ */
export function createCharacterComparison( export function createCharacterComparison<
T extends { name: string; id: string } | undefined = undefined,
>(
text: () => string, text: () => string,
fontA: () => { name: string; id: string }, fontA: () => T,
fontB: () => { name: string; id: string }, fontB: () => T,
weight: () => number, weight: () => number,
size: () => number, size: () => number,
) { ) {
let lines = $state<LineData[]>([]); let lines = $state<LineData[]>([]);
let containerWidth = $state(0); let containerWidth = $state(0);
function fontDefined<T extends { name: string; id: string }>(font: T | undefined): font is T {
return font !== undefined;
}
/** /**
* Measures text width using a canvas context. * Measures text width using a canvas context.
* @param ctx - Canvas rendering context * @param ctx - Canvas rendering context
@@ -36,10 +42,11 @@ export function createCharacterComparison(
function measureText( function measureText(
ctx: CanvasRenderingContext2D, ctx: CanvasRenderingContext2D,
text: string, text: string,
fontFamily: string,
fontSize: number, fontSize: number,
fontWeight: number, fontWeight: number,
fontFamily?: string,
): number { ): number {
if (!fontFamily) return 0;
ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`; ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
return ctx.measureText(text).width; return ctx.measureText(text).width;
} }
@@ -73,7 +80,7 @@ export function createCharacterComparison(
container: HTMLElement | undefined, container: HTMLElement | undefined,
measureCanvas: HTMLCanvasElement | undefined, measureCanvas: HTMLCanvasElement | undefined,
) { ) {
if (!container || !measureCanvas) return; if (!container || !measureCanvas || !fontA() || !fontB()) return;
const rect = container.getBoundingClientRect(); const rect = container.getBoundingClientRect();
containerWidth = rect.width; containerWidth = rect.width;
@@ -92,22 +99,24 @@ export function createCharacterComparison(
let currentLineWords: string[] = []; let currentLineWords: string[] = [];
function pushLine(words: string[]) { function pushLine(words: string[]) {
if (words.length === 0) return; if (words.length === 0 || !fontDefined(fontA()) || !fontDefined(fontB())) {
return;
}
const lineText = words.join(' '); const lineText = words.join(' ');
// Measure both fonts at the CURRENT weight // Measure both fonts at the CURRENT weight
const widthA = measureText( const widthA = measureText(
ctx!, ctx!,
lineText, lineText,
fontA().name,
Math.min(fontSize, controlledFontSize), Math.min(fontSize, controlledFontSize),
currentWeight, currentWeight,
fontA()?.name,
); );
const widthB = measureText( const widthB = measureText(
ctx!, ctx!,
lineText, lineText,
fontB().name,
Math.min(fontSize, controlledFontSize), Math.min(fontSize, controlledFontSize),
currentWeight, currentWeight,
fontB()?.name,
); );
const maxWidth = Math.max(widthA, widthB); const maxWidth = Math.max(widthA, widthB);
newLines.push({ text: lineText, width: maxWidth }); newLines.push({ text: lineText, width: maxWidth });
@@ -121,16 +130,16 @@ export function createCharacterComparison(
const widthA = measureText( const widthA = measureText(
ctx, ctx,
testLine, testLine,
fontA().name,
Math.min(fontSize, controlledFontSize), Math.min(fontSize, controlledFontSize),
currentWeight, currentWeight,
fontA()?.name,
); );
const widthB = measureText( const widthB = measureText(
ctx, ctx,
testLine, testLine,
fontB().name,
Math.min(fontSize, controlledFontSize), Math.min(fontSize, controlledFontSize),
currentWeight, currentWeight,
fontB()?.name,
); );
const maxWidth = Math.max(widthA, widthB); const maxWidth = Math.max(widthA, widthB);
const isContainerOverflown = maxWidth > availableWidth; const isContainerOverflown = maxWidth > availableWidth;
@@ -155,16 +164,16 @@ export function createCharacterComparison(
const wA = measureText( const wA = measureText(
ctx, ctx,
testFragment, testFragment,
fontA().name,
fontSize, fontSize,
currentWeight, currentWeight,
fontA()?.name,
); );
const wB = measureText( const wB = measureText(
ctx, ctx,
testFragment, testFragment,
fontB().name,
fontSize, fontSize,
currentWeight, currentWeight,
fontB()?.name,
); );
if (Math.max(wA, wB) <= availableWidth) { if (Math.max(wA, wB) <= availableWidth) {