152 lines
3.2 KiB
TypeScript
152 lines
3.2 KiB
TypeScript
|
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),
|
||
|
}));
|
||
|
};
|