Refactor card creation inputs and enhance canvas drawing functionality

This commit is contained in:
w 2025-07-15 01:28:14 -03:00
parent 28b933ecb8
commit 84fc17398d
3 changed files with 110 additions and 83 deletions

BIN
web/static/card-frame.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View file

@ -41,7 +41,6 @@ body {
}
.container {
max-width: 800px;
margin: 30px auto;
padding: 20px;
background-color: #ffffff;

View file

@ -2,11 +2,6 @@
{% block content %}
<style>
#cardOutput svg {
width: 100%;
height: auto;
max-width: 400px;
}
button {
background: #6aa1ff;
@ -42,93 +37,126 @@
<!-- Left: Inputs -->
<div style="flex: 1;">
<input type="text" id="name" placeholder="Card Name" style="width: 100%; margin-bottom: 0.5rem;"><br>
<input type="number" id="power" placeholder="⚡ Power" min="0" max="999" style="width: 100%; margin-bottom: 0.5rem;"><br>
<input type="number" id="charm" placeholder="❤️ Charm" min="0" max="999" style="width: 100%; margin-bottom: 0.5rem;"><br>
<input type="number" id="popularity" placeholder="💫 Popularity" min="0" max="999" style="width: 100%; margin-bottom: 0.5rem;"><br>
<input type="text" id="rarity" placeholder="Rarity (R / SR / SSR)" style="width: 100%; margin-bottom: 0.5rem;"><br>
<textarea id="flavor" placeholder="Flavor text..." rows="4" style="width: 100%; margin-bottom: 0.5rem;"></textarea><br>
<input type="text" id="nameInput" placeholder="Card Name" style="width: 100%; margin-bottom: 0.5rem;"><br>
<input type="text" id="packInput" placeholder="Card Name" style="width: 100%; margin-bottom: 0.5rem;"><br>
<input type="number" id="powerInput" placeholder="⚡ Power" min="0" max="999" style="width: 100%; margin-bottom: 0.5rem;"><br>
<input type="number" id="charmInput" placeholder="❤️ Charm" min="0" max="999" style="width: 100%; margin-bottom: 0.5rem;"><br>
<input type="number" id="popularityInput" placeholder="💫 Popularity" min="0" max="999" style="width: 100%; margin-bottom: 0.5rem;"><br>
<textarea id="flavorInput" placeholder="Flavor text..." rows="4" style="width: 100%; margin-bottom: 0.5rem;"></textarea><br>
<input type="file" id="uploadArt" style="margin-bottom: 0.5rem;"><br>
<button onclick="generateCard()">✨ Create Card</button>
<button id="downloadBtn" onclick="generateCard()">✨ Create Card</button>
</div>
<!-- Right: SVG Card Output -->
<div id="cardOutput" style="flex: 1; border: 1px solid #ccc; border-radius: 16px; overflow: hidden; background: #fff; box-shadow: 0 4px 10px rgba(0,0,0,0.1);">
<!-- SVG will go here -->
<div id="cardOutput" style="flex: 1; border: 1px solid #ccc; border-radius: 16px; background: #fff; box-shadow: 0 4px 10px rgba(0,0,0,0.1);">
<canvas id="cardCanvas" width="800" height="1120"></canvas>
</div>
<!-- Hidden SVG Template -->
<template id="svgTemplate">
<!-- Paste your full SVG here, with placeholders like:
[Card Name], ⚡ 99, ❤️ 99, 💫 99, SSR, [Flavor Text], and [Art Image Here]
-->
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="1120" viewBox="0 0 800 1120">
<defs>
<clipPath id="artClip">
<rect x="50" y="110" width="700" height="700" rx="20" ry="20"/>
</clipPath>
</defs>
<!-- Card Background -->
<rect x="0" y="0" width="800" height="1120" rx="40" ry="40" fill="#f9f9f9" stroke="#000" stroke-width="4"/>
<!-- Name Bar -->
<rect x="0" y="0" width="800" height="100" fill="#eee"/>
<text x="400" y="65" font-size="42" font-family="sans-serif" text-anchor="middle" fill="#222" id="cardName">[Card Name]</text>
<!-- Art Zone -->
<rect x="50" y="110" width="700" height="700" fill="#ddd"/>
<text x="400" y="470" font-size="24" font-family="sans-serif" text-anchor="middle" fill="#999" id="artPlaceholder">[Art Image Here]</text>
<!-- Stats -->
<text x="200" y="860" font-size="32" font-family="sans-serif" text-anchor="middle" fill="gold" id="powerStat">⚡ 99</text>
<text x="400" y="860" font-size="32" font-family="sans-serif" text-anchor="middle" fill="red" id="charmStat">❤️ 99</text>
<text x="600" y="860" font-size="32" font-family="sans-serif" text-anchor="middle" fill="blue" id="popularityStat">💫 99</text>
<!-- Flavor Text -->
<rect x="50" y="890" width="700" height="130" fill="#fff8e1" stroke="#000" stroke-width="1"/>
<text x="400" y="960" font-size="20" font-family="serif" text-anchor="middle" fill="#444" id="flavorText">[Flavor Text]</text>
<!-- Rarity Marker -->
<text x="750" y="70" font-size="48" font-family="sans-serif" text-anchor="end" fill="#a0a" id="rarityMark">SSR</text>
<!-- Series Footer -->
<text x="400" y="1110" font-size="18" font-family="sans-serif" text-anchor="middle" fill="#888">From the Fediverse Gacha Series</text>
</svg>
</template>
</div>
<script>
function generateCard() {
const template = document.getElementById("svgTemplate");
const svg = template.content.cloneNode(true);
const canvas = document.getElementById("cardCanvas");
const ctx = canvas.getContext("2d");
svg.getElementById("cardName").textContent = document.getElementById("name").value;
svg.getElementById("powerStat").textContent = `⚡ ${document.getElementById("power").value}`;
svg.getElementById("charmStat").textContent = `❤️ ${document.getElementById("charm").value}`;
svg.getElementById("popularityStat").textContent = `💫 ${document.getElementById("popularity").value}`;
svg.getElementById("rarityMark").textContent = document.getElementById("rarity").value;
svg.getElementById("flavorText").textContent = document.getElementById("flavor").value;
const nameInput = document.getElementById("nameInput");
const packInput = document.getElementById("packInput");
const powerInput = document.getElementById("powerInput");
const charmInput = document.getElementById("charmInput");
const popularityInput = document.getElementById("popularityInput");
const flavorInput = document.getElementById("flavorInput");
const artPlaceholder = svg.getElementById("artPlaceholder");
const file = document.getElementById("uploadArt").files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
const img = document.createElementNS("http://www.w3.org/2000/svg", "image");
img.setAttributeNS(null, "href", e.target.result);
img.setAttributeNS(null, "x", 50);
img.setAttributeNS(null, "y", 110);
img.setAttributeNS(null, "width", 700);
img.setAttributeNS(null, "height", 700);
img.setAttributeNS(null, "clip-path", "url(#artClip)");
artPlaceholder.replaceWith(img);
document.getElementById("cardOutput").innerHTML = '';
document.getElementById("cardOutput").appendChild(svg);
const downloadBtn = document.getElementById("downloadBtn");
// Generate a unique ID for the card based on pack and card name
async function generateCardId(packName, cardName) {
const encoder = new TextEncoder();
const data = encoder.encode(packName + ':' + cardName);
const hashBuffer = await crypto.subtle.digest('SHA-1', data);
// Convert hash to hex
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
// Return first 12 characters
return hex.slice(0, 12);
}
// Load the card frame image
const frameImage = new Image();
frameImage.src = "{{ url_for('static', filename='card-frame.png') }}"; // <-- replace with your transparent card frame PNG
frameImage.onload = () => {
drawCard();
};
reader.readAsDataURL(file);
function drawCard() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#f5f5f5";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(frameImage, 0, 0, canvas.width, canvas.height);
// Name & pack
ctx.fillStyle = "#FFFFFF";
ctx.font = "bold 40px sans-serif";
ctx.textAlign = "center";
ctx.fillText(nameInput.value, canvas.width / 2, 100);
ctx.font = "20px sans-serif";
ctx.fillText(packInput.value, canvas.width / 2, 140);
ctx.textAlign = "left";
// Stats
ctx.font = "30px sans-serif";
ctx.fillText("Power: " + powerInput.value, 60, 1050);
ctx.fillText("Charm: " + charmInput.value, 300, 1050);
ctx.fillText("Popularity: " + popularityInput.value, 540, 1050);
// Flavor
ctx.font = "italic 24px serif";
wrapText(flavorInput.value, 60, 950, 680, 30);
// Defer ID drawing to a separate async step!
drawCardId();
}
async function drawCardId() {
const id = await generateCardId(packInput.value, nameInput.value);
ctx.font = "20px sans-serif";
ctx.fillText("KC-" + id, canvas.width - 350, canvas.height - 60);
}
function wrapText(text, x, y, maxWidth, lineHeight) {
const words = text.split(" ");
let line = "";
for (let n = 0; n < words.length; n++) {
const testLine = line + words[n] + " ";
const metrics = ctx.measureText(testLine);
const testWidth = metrics.width;
if (testWidth > maxWidth && n > 0) {
ctx.fillText(line, x, y);
line = words[n] + " ";
y += lineHeight;
} else {
document.getElementById("cardOutput").innerHTML = '';
document.getElementById("cardOutput").appendChild(svg);
line = testLine;
}
}
ctx.fillText(line, x, y);
}
// Update card live when inputs change
[nameInput, powerInput, charmInput, popularityInput, flavorInput,packInput].forEach(input => {
input.addEventListener("input", drawCard);
});
// Download button
downloadBtn.addEventListener("click", () => {
const link = document.createElement("a");
link.download = "kemoverse-card.png";
link.href = canvas.toDataURL("image/png");
link.click();
});
</script>
{% endblock %}