improve some drawing
This commit is contained in:
parent
3bb0308949
commit
f497057dae
@ -1,6 +1,8 @@
|
||||
import { useState } from "react";
|
||||
import { sampleCard } from "../sampleData.ts";
|
||||
import { Card } from "./Card.tsx";
|
||||
|
||||
export const App = () => {
|
||||
return <div><Card card={sampleCard}/></div>;
|
||||
const [count, setCount] = useState(0);
|
||||
return <div><Card key={count} card={sampleCard}/><button onClick={() => {setCount(c => c+1)}}>Rerender (for fonts)</button></div>;
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
137
src/draw.ts
137
src/draw.ts
@ -5,8 +5,6 @@ export const loadImage = (
|
||||
key: string,
|
||||
src: string
|
||||
): Promise<HTMLImageElement> => {
|
||||
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
|
||||
|
25
src/richtext.ts
Normal file
25
src/richtext.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// type RichnessNodeDefinition<N extends {type: string}> = {
|
||||
// type: N["type"]
|
||||
// measure(context: CanvasRenderingContext2D, node: N): Promise<TextMetrics>;
|
||||
// render(
|
||||
// context: CanvasRenderingContext2D,
|
||||
// node: N,
|
||||
// x: number,
|
||||
// y: number
|
||||
// ): Promise<void>;
|
||||
// };
|
||||
|
||||
// type Richness<N extends {type: string}> = {[K in N["type"]]: RichnessNodeDefinition<N & {type: K}>}
|
||||
|
||||
// const drawRichText = <N extends {type: string}>(
|
||||
// context: CanvasRenderingContext2D,
|
||||
// richness: Richness<N>,
|
||||
// richText: N[],
|
||||
// x: number,
|
||||
// y: number,
|
||||
// maxWidth: number,
|
||||
// ) => {
|
||||
// context.save();
|
||||
// const
|
||||
// context.restore();
|
||||
// };
|
Loading…
x
Reference in New Issue
Block a user