start on making a text framework
This commit is contained in:
parent
1e6e336f73
commit
8e7bcc185c
151
src/dominiontext.ts
Normal file
151
src/dominiontext.ts
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
type Piece =
|
||||||
|
| { type: "text"; text: string; isBold?: boolean; isItalic?: boolean }
|
||||||
|
| { type: "space" }
|
||||||
|
| { type: "break" }
|
||||||
|
| { type: "coin"; text: string };
|
||||||
|
|
||||||
|
type PromiseOr<T> = T | Promise<T>;
|
||||||
|
|
||||||
|
type PieceMeasure = {
|
||||||
|
type: "content" | "space" | "break";
|
||||||
|
width: number;
|
||||||
|
ascent: number;
|
||||||
|
descent: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PieceDef<T extends Piece["type"], M extends PieceMeasure> = {
|
||||||
|
type: T;
|
||||||
|
measure(
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
piece: Piece & { type: T }
|
||||||
|
): PromiseOr<M>;
|
||||||
|
render(
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
piece: Piece & { type: T },
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
measure: NoInfer<M>
|
||||||
|
): PromiseOr<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const pieceDef = <T extends Piece["type"], M extends PieceMeasure>(
|
||||||
|
def: PieceDef<T, M>
|
||||||
|
) => {
|
||||||
|
return def;
|
||||||
|
};
|
||||||
|
|
||||||
|
const textPiece = pieceDef({
|
||||||
|
type: "text",
|
||||||
|
measure(context, piece) {
|
||||||
|
const metrics = context.measureText(piece.text);
|
||||||
|
return {
|
||||||
|
type: "content",
|
||||||
|
width: metrics.width,
|
||||||
|
ascent: metrics.emHeightAscent,
|
||||||
|
descent: metrics.emHeightDescent,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
render(context, piece, x, y) {
|
||||||
|
context.fillText(piece.text, x, y);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const spacePiece = pieceDef({
|
||||||
|
type: "space",
|
||||||
|
measure(context, _piece) {
|
||||||
|
const metrics = context.measureText(" ");
|
||||||
|
return {
|
||||||
|
type: "space",
|
||||||
|
width: metrics.width,
|
||||||
|
ascent: metrics.emHeightAscent,
|
||||||
|
descent: metrics.emHeightDescent,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
render() {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const breakPiece = pieceDef({
|
||||||
|
type: "break",
|
||||||
|
measure(context, _piece) {
|
||||||
|
const metrics = context.measureText(" ");
|
||||||
|
return {
|
||||||
|
type: "break",
|
||||||
|
width: 0,
|
||||||
|
ascent: metrics.emHeightAscent,
|
||||||
|
descent: metrics.emHeightDescent,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
render() {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const coinPiece = pieceDef({
|
||||||
|
type: "coin",
|
||||||
|
measure(context, _piece) {
|
||||||
|
const metrics = context.measureText(" ");
|
||||||
|
return {
|
||||||
|
type: "content",
|
||||||
|
width: metrics.emHeightAscent + metrics.emHeightDescent,
|
||||||
|
ascent: metrics.emHeightAscent,
|
||||||
|
descent: metrics.emHeightDescent,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
render(context, piece, x, y, measure) {
|
||||||
|
context.save();
|
||||||
|
context.fillStyle = "yellow";
|
||||||
|
context.fillRect(
|
||||||
|
x,
|
||||||
|
y - measure.ascent,
|
||||||
|
measure.width,
|
||||||
|
measure.ascent + measure.descent
|
||||||
|
);
|
||||||
|
context.fillStyle = "black";
|
||||||
|
context.fillText(piece.text, x, y);
|
||||||
|
context.restore();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pieceDefs = [textPiece, spacePiece, breakPiece, coinPiece];
|
||||||
|
|
||||||
|
const measurePiece = (context: CanvasRenderingContext2D, piece: Piece) => {
|
||||||
|
const def = pieceDefs.find((def) => def.type === piece.type)!;
|
||||||
|
return def.measure(context, piece as any);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderPiece = (
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
piece: Piece,
|
||||||
|
x: number,
|
||||||
|
y: number
|
||||||
|
) => {
|
||||||
|
const def = pieceDefs.find((def) => def.type === piece.type)!;
|
||||||
|
const measure = def.measure(context, piece as any);
|
||||||
|
return def.render(context, piece as any, x, y, measure as any);
|
||||||
|
};
|
||||||
|
|
||||||
|
// export const drawDominionText = (
|
||||||
|
// context: CanvasRenderingContext2D,
|
||||||
|
// text: Piece[],
|
||||||
|
// x: number,
|
||||||
|
// y: number,
|
||||||
|
// w: number,
|
||||||
|
// h: number
|
||||||
|
// ) => {};
|
||||||
|
|
||||||
|
type DominionFont = {
|
||||||
|
font: "text" | "title";
|
||||||
|
size: number;
|
||||||
|
isBold: boolean;
|
||||||
|
isItalic: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const measureDominionText = (
|
||||||
|
context: CanvasRenderingContext2D,
|
||||||
|
pieces: Piece[],
|
||||||
|
font: DominionFont,
|
||||||
|
maxWidth: number
|
||||||
|
) => {
|
||||||
|
const data = pieces.map((piece) => ({
|
||||||
|
piece,
|
||||||
|
measure: measurePiece(context, piece),
|
||||||
|
}));
|
||||||
|
};
|
@ -94,7 +94,7 @@ const wrapText = (
|
|||||||
return text.split("\n").flatMap((paragraph) => {
|
return text.split("\n").flatMap((paragraph) => {
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
let words = 0;
|
let words = 0;
|
||||||
let remainingText = paragraph.trim().replace(/\s+/g, " ");
|
let remainingText = paragraph.trim().replace(/ +/g, " ");
|
||||||
let oldLine = "";
|
let oldLine = "";
|
||||||
let countdown = 100;
|
let countdown = 100;
|
||||||
while (remainingText.length > 0) {
|
while (remainingText.length > 0) {
|
||||||
@ -131,7 +131,7 @@ const measureText = (
|
|||||||
allowWrap: boolean | undefined
|
allowWrap: boolean | undefined
|
||||||
) => {
|
) => {
|
||||||
const measure = context.measureText(text);
|
const measure = context.measureText(text);
|
||||||
const lineHeight = measure.emHeightAscent + measure.emHeightDescent;
|
const lineHeight = 1.2 * (measure.emHeightAscent + measure.emHeightDescent);
|
||||||
if (!allowWrap || !maxWidth) {
|
if (!allowWrap || !maxWidth) {
|
||||||
return {
|
return {
|
||||||
width: measure.width,
|
width: measure.width,
|
||||||
@ -193,7 +193,7 @@ const drawTextCentered = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const measure = context.measureText(text);
|
const measure = context.measureText(text);
|
||||||
const lineHeight = measure.emHeightAscent + measure.emHeightDescent;
|
const lineHeight = 1.2 * (measure.emHeightAscent + measure.emHeightDescent);
|
||||||
context.textAlign = "center";
|
context.textAlign = "center";
|
||||||
context.textBaseline = "middle";
|
context.textBaseline = "middle";
|
||||||
if (allowWrap && maxWidth) {
|
if (allowWrap && maxWidth) {
|
||||||
@ -235,7 +235,7 @@ const drawStandardCard = async (
|
|||||||
// Draw the description
|
// Draw the description
|
||||||
drawTextCentered(
|
drawTextCentered(
|
||||||
context,
|
context,
|
||||||
"You may play an Action card from your hand.",
|
"You may play an Action card from your hand costing up to \u202f◯\u202f.",
|
||||||
w / 2,
|
w / 2,
|
||||||
1520,
|
1520,
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user