cheetah-grid
Version:
Cheetah Grid is a high performance grid engine that works on canvas
112 lines (106 loc) • 2.68 kB
text/typescript
import type { RectProps } from "../ts-types";
export type PaddingOption = {
left?: number;
right?: number;
top?: number;
bottom?: number;
};
const fontSizeCache: {
[key: string]: {
width: number;
height: number;
};
} = {};
export function getFontSize(
ctx: CanvasRenderingContext2D,
font: string | null | undefined
): {
width: number;
height: number;
} {
const fontName = font || ctx.font;
if (fontSizeCache[fontName]) {
return fontSizeCache[fontName];
}
const bk = ctx.font;
// To calculate the size of the letters correctly, letterSpacing must be 'normal'.
// `ctx.letterSpacing` is not used yet because it is Baseline 2024.
ctx.canvas.style.letterSpacing = "normal";
try {
ctx.font = fontName;
const em = ctx.measureText("あ").width;
return (fontSizeCache[fontName] = {
width: em,
height: em,
});
} finally {
ctx.canvas.style.letterSpacing = "";
ctx.font = bk;
}
}
export function calcBasePosition(
ctx: CanvasRenderingContext2D,
rect: RectProps,
{
offset = 0,
padding: {
left: paddingLeft = 0,
right: paddingRight = 0,
top: paddingTop = 0,
bottom: paddingBottom = 0,
} = {},
}: {
offset?: number;
padding?: PaddingOption;
} = {}
): { x: number; y: number } {
return calcStartPosition(ctx, rect, 0, 0, {
offset,
padding: {
left: paddingLeft,
right: paddingRight,
top: paddingTop,
bottom: paddingBottom,
},
});
}
export function calcStartPosition(
ctx: CanvasRenderingContext2D,
rect: RectProps,
width: number,
height: number,
{
offset = 0,
padding: {
left: paddingLeft = 0,
right: paddingRight = 0,
top: paddingTop = 0,
bottom: paddingBottom = 0,
} = {},
}: {
offset?: number;
padding?: PaddingOption;
} = {}
): { x: number; y: number } {
const textAlign = ctx.textAlign || "left";
const textBaseline = ctx.textBaseline || "middle";
ctx.textAlign = textAlign;
ctx.textBaseline = textBaseline;
let x = rect.left + offset + paddingLeft;
if (textAlign === "right" || textAlign === "end") {
x = rect.right - width - offset - paddingRight;
} else if (textAlign === "center") {
x = rect.left + (rect.width - width + paddingLeft - paddingRight) / 2;
}
let y = rect.top + offset + paddingTop;
if (
textBaseline === "bottom" ||
textBaseline === "alphabetic" ||
textBaseline === "ideographic"
) {
y = rect.bottom - height - offset - paddingBottom;
} else if (textBaseline === "middle") {
y = rect.top + (rect.height - height + paddingTop - paddingBottom) / 2;
}
return { x, y };
}