Files
pihkaal-me/app/utils/canvas.ts

194 lines
4.7 KiB
TypeScript

export const fillTextCentered = (
ctx: CanvasRenderingContext2D,
text: string,
x: number,
y: number,
width: number,
): number => {
const measure = ctx.measureText(text);
const textX = Math.floor(x + width / 2 - measure.actualBoundingBoxRight / 2);
const textY =
measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent;
ctx.fillText(text, textX, y + textY);
return measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent + 1;
};
export const fillImageTextHCentered = (
ctx: CanvasRenderingContext2D,
image: AtlasImage,
text: string,
x: number,
y: number,
width: number,
gap: number,
) => {
const { actualBoundingBoxRight: textWidth } = ctx.measureText(text);
const totalWidth = textWidth + gap + image.rect.width;
const groupX = Math.floor(x + width / 2 - totalWidth / 2);
image.draw(ctx, groupX, y);
ctx.fillText(text, groupX + gap + image.rect.width, y);
};
export const fillTextHCentered = (
ctx: CanvasRenderingContext2D,
text: string,
x: number,
y: number,
width: number,
) => {
const measure = ctx.measureText(text);
const textX = Math.floor(x + width / 2 - measure.actualBoundingBoxRight / 2);
ctx.fillText(text, textX, y);
};
export const fillTextHCenteredMultiline = (
ctx: CanvasRenderingContext2D,
text: string,
x: number,
y: number,
width: number,
lineHeight: number,
) => {
const lines = text.split("\n");
for (let i = 0, y = 20; i < lines.length; i += 1, y += lineHeight) {
fillTextHCentered(ctx, lines[i]!, 0, y, width);
}
};
export const CHECKBOX_SIZE = 7;
export const CHECKBOX_TEXT_GAP = 5;
export const drawCheckbox = (
ctx: CanvasRenderingContext2D,
x: number,
y: number,
checked: boolean,
) => {
ctx.fillRect(x, y, CHECKBOX_SIZE, 1);
ctx.fillRect(x, y + CHECKBOX_SIZE - 1, CHECKBOX_SIZE, 1);
ctx.fillRect(x, y + 1, 1, CHECKBOX_SIZE - 2);
ctx.fillRect(x + CHECKBOX_SIZE - 1, y + 1, 1, CHECKBOX_SIZE - 2);
if (checked) {
for (let i = 2; i < CHECKBOX_SIZE - 2; i++) {
ctx.fillRect(x + i, y + i, 1, 1);
ctx.fillRect(x + CHECKBOX_SIZE - 1 - i, y + i, 1, 1);
}
}
};
export const fillCirclePixelated = (
ctx: CanvasRenderingContext2D,
cx: number,
cy: number,
radius: number,
) => {
const r = Math.floor(radius);
for (let dy = -r; dy <= r; dy++) {
for (let dx = -r; dx <= r; dx++) {
if (dx * dx + dy * dy <= r * r) {
ctx.fillRect(Math.floor(cx) + dx, Math.floor(cy) + dy, 1, 1);
}
}
}
};
export const strokeCirclePixelated = (
ctx: CanvasRenderingContext2D,
cx: number,
cy: number,
radius: number,
thickness: number = 2,
) => {
const outerR = Math.floor(radius);
const innerR = Math.floor(radius - thickness);
for (let dy = -outerR; dy <= outerR; dy++) {
for (let dx = -outerR; dx <= outerR; dx++) {
const distSq = dx * dx + dy * dy;
if (distSq <= outerR * outerR && distSq > innerR * innerR) {
ctx.fillRect(Math.floor(cx) + dx, Math.floor(cy) + dy, 1, 1);
}
}
}
};
export const fillTextWordWrapped = (
ctx: CanvasRenderingContext2D,
text: string,
x: number,
y: number,
width: number,
lineHeight?: number,
): number => {
const words = text.split(" ");
let line = "";
let currentY = y;
let lineCount = 0;
const height =
lineHeight ||
ctx.measureText("M").actualBoundingBoxAscent +
ctx.measureText("M").actualBoundingBoxDescent;
currentY += height;
for (let i = 0; i < words.length; i++) {
const testLine = line + (line ? " " : "") + words[i];
const metrics = ctx.measureText(testLine);
const testWidth = metrics.width;
if (testWidth > width && line) {
ctx.fillText(line, x, currentY);
line = words[i]!;
currentY += height;
lineCount++;
} else {
line = testLine;
}
}
if (line) {
ctx.fillText(line, x, currentY);
lineCount++;
}
return lineCount * height;
};
export const drawButton = (
ctx: CanvasRenderingContext2D,
text: string,
x: number,
y: number,
width: number,
) => {
const { assets } = useAssets((a) => a.images.common);
const LEFT_WIDTH = assets.buttonLeft.rect.width;
const RIGHT_WIDTH = assets.buttonRight.rect.width;
const BODY_WIDTH = assets.buttonBody.rect.width;
const bodySpace = width - LEFT_WIDTH - RIGHT_WIDTH;
const bodyCount = Math.ceil(bodySpace / BODY_WIDTH);
ctx.save();
ctx.translate(x, y);
assets.buttonLeft.draw(ctx, 0, 0);
for (let i = 0; i < bodyCount; i++) {
assets.buttonBody.draw(ctx, LEFT_WIDTH + i * BODY_WIDTH, 0);
}
assets.buttonRight.draw(ctx, width - RIGHT_WIDTH, 0);
ctx.textBaseline = "top";
ctx.font = "10px NDS10";
ctx.fillStyle = "#494118";
fillTextHCentered(ctx, text, 1, 4, width);
ctx.restore();
};