diff --git a/src/client/App.tsx b/src/client/App.tsx
index 65caac1..70a0aac 100644
--- a/src/client/App.tsx
+++ b/src/client/App.tsx
@@ -1,6 +1,8 @@
+import { useState } from "react";
import { sampleCard } from "../sampleData.ts";
import { Card } from "./Card.tsx";
export const App = () => {
- return
;
+ const [count, setCount] = useState(0);
+ return
;
};
diff --git a/src/client/Card.tsx b/src/client/Card.tsx
index 7b22ee7..e4eee4a 100644
--- a/src/client/Card.tsx
+++ b/src/client/Card.tsx
@@ -19,9 +19,8 @@ export const Card = (props: {card: DominionCard}) => {
if (canvasElement) {
const context = canvasElement.getContext("2d");
if (context) {
- console.log("loading");
- await loadImages()
- console.log("done loading");
+ await loadImages();
+ // await loadFonts();
drawCard(context, card);
}
}
diff --git a/src/draw.ts b/src/draw.ts
index 84fa6fb..3c20c52 100644
--- a/src/draw.ts
+++ b/src/draw.ts
@@ -5,8 +5,6 @@ export const loadImage = (
key: string,
src: string
): Promise => {
- console.log("2", key);
- console.log(src);
return new Promise((resolve) => {
if (key in imageCache && imageCache[key]) {
resolve(imageCache[key]);
@@ -45,7 +43,6 @@ const imageList = [
export const loadImages = async () => {
for (const imageInfo of imageList) {
const { key, src } = imageInfo;
- console.log(key);
await loadImage(key, src);
}
};
@@ -88,22 +85,130 @@ export const drawCard = (
}
};
-const drawText = (
+const wrapText = (
+ context: CanvasRenderingContext2D,
+ text: string,
+ w: number
+) => {
+ return text.split("\n").flatMap((paragraph) => {
+ const lines: string[] = [];
+ let words = 0;
+ let remainingText = paragraph.trim().replace(/\s+/g, " ");
+ let oldLine = "";
+ let countdown = 100;
+ while (remainingText.length > 0) {
+ countdown--;
+ if (countdown <= 0) {
+ console.log("CUT SHORT");
+ return [];
+ }
+ words++;
+ const newLine = remainingText.split(" ").slice(0, words).join(" ");
+ const metrics = context.measureText(newLine);
+ if (metrics.width > w) {
+ words = 0;
+ lines.push(oldLine);
+ remainingText = remainingText.slice(oldLine.length).trim();
+ } else if (newLine.length >= remainingText.length) {
+ words = 0;
+ lines.push(newLine);
+ remainingText = "";
+ }
+ oldLine = newLine;
+ }
+ if (!lines.length) {
+ return [""];
+ }
+ return lines;
+ });
+};
+
+const measureText = (
+ context: CanvasRenderingContext2D,
+ text: string,
+ maxWidth: number | undefined,
+ allowWrap: boolean | undefined
+) => {
+ const measure = context.measureText(text);
+ const lineHeight = measure.emHeightAscent + measure.emHeightDescent;
+ if (!allowWrap || !maxWidth) {
+ return {
+ width: measure.width,
+ height: lineHeight,
+ };
+ }
+ const lines = wrapText(context, text, maxWidth);
+ const width = Math.max(
+ ...lines.map((line) => context.measureText(line).width)
+ );
+ const height = lines.length * lineHeight;
+ return { width, height };
+};
+
+const drawTextCentered = (
context: CanvasRenderingContext2D,
text: string,
x: number,
y: number,
options?: {
+ defaultSize?: number;
maxWidth?: number;
maxHeight?: number;
allowWrap?: boolean;
font?: string;
+ fontWeight?: "normal" | "bold";
color?: string;
}
) => {
- const { maxWidth = undefined } = options ?? {};
- context.font = "bold 48px serif";
- context.fillText(text, x, y, maxWidth);
+ const {
+ defaultSize = 75,
+ maxWidth,
+ maxHeight,
+ font = "DominionText",
+ fontWeight = "normal",
+ color,
+ allowWrap = false,
+ } = options ?? {};
+ context.save();
+ if (color) {
+ context.fillStyle = color;
+ }
+ let size = defaultSize;
+ context.font = `${fontWeight} ${size}pt ${font}`;
+ if (maxWidth) {
+ while (
+ measureText(context, text, maxWidth, allowWrap).width > maxWidth
+ ) {
+ size -= 2;
+ context.font = `${fontWeight} ${size}pt ${font}`;
+ }
+ }
+ if (maxHeight) {
+ while (
+ measureText(context, text, maxWidth, allowWrap).height > maxHeight
+ ) {
+ size -= 1;
+ context.font = `${fontWeight} ${size}pt ${font}`;
+ }
+ }
+ const measure = context.measureText(text);
+ const lineHeight = measure.emHeightAscent + measure.emHeightDescent;
+ context.textAlign = "center";
+ context.textBaseline = "middle";
+ if (allowWrap && maxWidth) {
+ const lines = wrapText(context, text, maxWidth);
+ lines.forEach((line, i) => {
+ context.fillText(
+ line,
+ x,
+ y - (lineHeight * lines.length) / 2 + lineHeight * i,
+ maxWidth
+ );
+ });
+ } else {
+ context.fillText(text, x, y, maxWidth);
+ }
+ context.restore();
};
const drawStandardCard = async (
@@ -121,8 +226,24 @@ const drawStandardCard = async (
context.drawImage(colorImage(getImage("card-brown"), "#ff9911"), 0, 0);
context.drawImage(getImage("card-description-focus"), 44, 1094);
// Draw the name
- drawText(context, card.title, 400, 500);
+ drawTextCentered(context, "Moonlit Scheme", w / 2, 220, {
+ maxWidth: 1100,
+ font: "DominionTitle",
+ fontWeight: "bold",
+ });
// Draw the description
+ drawTextCentered(
+ context,
+ "You may play an Action card from your hand.",
+ w / 2,
+ 1520,
+ {
+ maxWidth: 1100,
+ font: "DominionText",
+ allowWrap: true,
+ defaultSize: 60,
+ }
+ );
// Draw the types
// Draw the cost
// Draw the preview
diff --git a/src/richtext.ts b/src/richtext.ts
new file mode 100644
index 0000000..ca9edcc
--- /dev/null
+++ b/src/richtext.ts
@@ -0,0 +1,25 @@
+// type RichnessNodeDefinition = {
+// type: N["type"]
+// measure(context: CanvasRenderingContext2D, node: N): Promise;
+// render(
+// context: CanvasRenderingContext2D,
+// node: N,
+// x: number,
+// y: number
+// ): Promise;
+// };
+
+// type Richness = {[K in N["type"]]: RichnessNodeDefinition}
+
+// const drawRichText = (
+// context: CanvasRenderingContext2D,
+// richness: Richness,
+// richText: N[],
+// x: number,
+// y: number,
+// maxWidth: number,
+// ) => {
+// context.save();
+// const
+// context.restore();
+// };