Compare commits
9 commits
84fc17398d
...
c66155e933
Author | SHA1 | Date | |
---|---|---|---|
c66155e933 | |||
be998ba928 | |||
983325a2e2 | |||
7d7cd1bb46 | |||
8ae6d2cb38 | |||
18b1eef26e | |||
eff962608f | |||
41505776af | |||
05711c22f2 |
6 changed files with 82 additions and 37 deletions
Binary file not shown.
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
BIN
web/static/v1_common.png
Normal file
BIN
web/static/v1_common.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 57 KiB |
BIN
web/static/v1_epic.png
Normal file
BIN
web/static/v1_epic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 57 KiB |
BIN
web/static/v1_rare.png
Normal file
BIN
web/static/v1_rare.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 57 KiB |
BIN
web/static/v1_uncommon.png
Normal file
BIN
web/static/v1_uncommon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
|
@ -43,9 +43,17 @@
|
||||||
<input type="text" id="packInput" 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="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="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>
|
<input type="number" id="witInput" placeholder="💫 Wit" 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>
|
<textarea id="flavorInput" placeholder="Flavor text..." rows="4" style="width: 100%; margin-bottom: 0.5rem;"></textarea><br>
|
||||||
|
<input type="text" id="artistInput" placeholder="Artist Name" style="width: 100%; margin-bottom: 0.5rem;"><br>
|
||||||
<input type="file" id="uploadArt" style="margin-bottom: 0.5rem;"><br>
|
<input type="file" id="uploadArt" style="margin-bottom: 0.5rem;"><br>
|
||||||
|
<select id="frameSelect" style="width: 100%; margin-bottom: 0.5rem;">
|
||||||
|
<option value="v1_common.png" selected>Common</option>
|
||||||
|
<option value="v1_uncommon.png">Uncommon</option>
|
||||||
|
<option value="v1_rare.png">Rare</option>
|
||||||
|
<option value="v1_epic.png">Epic</option>
|
||||||
|
<option value="v1_legendary.png">Legendary</option>
|
||||||
|
</select>
|
||||||
<button id="downloadBtn" onclick="generateCard()">✨ Create Card</button>
|
<button id="downloadBtn" onclick="generateCard()">✨ Create Card</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- Right: SVG Card Output -->
|
<!-- Right: SVG Card Output -->
|
||||||
|
@ -64,15 +72,20 @@ const nameInput = document.getElementById("nameInput");
|
||||||
const packInput = document.getElementById("packInput");
|
const packInput = document.getElementById("packInput");
|
||||||
const powerInput = document.getElementById("powerInput");
|
const powerInput = document.getElementById("powerInput");
|
||||||
const charmInput = document.getElementById("charmInput");
|
const charmInput = document.getElementById("charmInput");
|
||||||
const popularityInput = document.getElementById("popularityInput");
|
const witInput = document.getElementById("witInput");
|
||||||
const flavorInput = document.getElementById("flavorInput");
|
const flavorInput = document.getElementById("flavorInput");
|
||||||
|
const artistInput = document.getElementById("artistInput");
|
||||||
|
|
||||||
const downloadBtn = document.getElementById("downloadBtn");
|
const downloadBtn = document.getElementById("downloadBtn");
|
||||||
|
const uploadArt = document.getElementById("uploadArt");
|
||||||
|
const frameSelect = document.getElementById("frameSelect");
|
||||||
|
let frameImage = new Image();
|
||||||
|
let uploadedImage = null;
|
||||||
|
|
||||||
// Generate a unique ID for the card based on pack and card name
|
// Generate a unique ID for the card based on pack and card name
|
||||||
async function generateCardId(packName, cardName) {
|
async function generateCardId(...args) {
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
const data = encoder.encode(packName + ':' + cardName);
|
const data = encoder.encode(args.join(':'));
|
||||||
const hashBuffer = await crypto.subtle.digest('SHA-1', data);
|
const hashBuffer = await crypto.subtle.digest('SHA-1', data);
|
||||||
|
|
||||||
// Convert hash to hex
|
// Convert hash to hex
|
||||||
|
@ -83,18 +96,40 @@ async function generateCardId(packName, cardName) {
|
||||||
return hex.slice(0, 12);
|
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 = () => {
|
uploadArt.addEventListener("change", function(e) {
|
||||||
drawCard();
|
const file = e.target.files[0];
|
||||||
};
|
if (!file) return;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function(event) {
|
||||||
|
uploadedImage = new Image();
|
||||||
|
uploadedImage.onload = drawCard;
|
||||||
|
uploadedImage.src = event.target.result;
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update frame image when selection changes
|
||||||
|
function updateFrame() {
|
||||||
|
frameImage.src = "{{ url_for('static', filename='') }}" + frameSelect.value;
|
||||||
|
frameImage.onload = drawCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial frame load
|
||||||
|
updateFrame();
|
||||||
|
|
||||||
|
// Change frame when dropdown changes
|
||||||
|
frameSelect.addEventListener("change", updateFrame);
|
||||||
|
|
||||||
function drawCard() {
|
function drawCard() {
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
ctx.fillStyle = "#f5f5f5";
|
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
|
// Draw uploaded art as background if present
|
||||||
|
if (uploadedImage) {
|
||||||
|
ctx.drawImage(uploadedImage, 0, 0, canvas.width, canvas.height);
|
||||||
|
}
|
||||||
|
|
||||||
ctx.drawImage(frameImage, 0, 0, canvas.width, canvas.height);
|
ctx.drawImage(frameImage, 0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
// Name & pack
|
// Name & pack
|
||||||
|
@ -103,60 +138,70 @@ function drawCard() {
|
||||||
ctx.textAlign = "center";
|
ctx.textAlign = "center";
|
||||||
ctx.fillText(nameInput.value, canvas.width / 2, 100);
|
ctx.fillText(nameInput.value, canvas.width / 2, 100);
|
||||||
ctx.font = "20px sans-serif";
|
ctx.font = "20px sans-serif";
|
||||||
ctx.fillText(packInput.value, canvas.width / 2, 140);
|
ctx.fillText(packInput.value, canvas.width / 2, 160);
|
||||||
ctx.textAlign = "left";
|
ctx.textAlign = "left";
|
||||||
|
|
||||||
// Stats
|
// Stats
|
||||||
ctx.font = "30px sans-serif";
|
ctx.font = "30px sans-serif";
|
||||||
ctx.fillText("Power: " + powerInput.value, 60, 1050);
|
ctx.fillText(powerInput.value, 220, 823);
|
||||||
ctx.fillText("Charm: " + charmInput.value, 300, 1050);
|
ctx.fillText(charmInput.value, 373, 823);
|
||||||
ctx.fillText("Popularity: " + popularityInput.value, 540, 1050);
|
ctx.fillText(witInput.value, 512, 823);
|
||||||
|
|
||||||
// Flavor
|
// Flavor
|
||||||
ctx.font = "italic 24px serif";
|
ctx.font = "italic 24px serif";
|
||||||
wrapText(flavorInput.value, 60, 950, 680, 30);
|
ctx.textAlign = "center";
|
||||||
|
wrapText(flavorInput.value, canvas.width / 2, 965, 558, 30);
|
||||||
|
|
||||||
|
// Artist
|
||||||
|
ctx.font = "30px sans-serif";
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.fillStyle = "#000000";
|
||||||
|
ctx.fillText(artistInput.value, canvas.width / 2, 1060);
|
||||||
|
|
||||||
// Defer ID drawing to a separate async step!
|
// Defer ID drawing to a separate async step!
|
||||||
drawCardId();
|
drawCardId();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function drawCardId() {
|
async function drawCardId() {
|
||||||
const id = await generateCardId(packInput.value, nameInput.value);
|
const id = await generateCardId(packInput.value, nameInput.value, powerInput.value, charmInput.value, witInput.value, artistInput.value);
|
||||||
ctx.font = "20px sans-serif";
|
ctx.font = "15px sans-serif";
|
||||||
ctx.fillText("KC-" + id, canvas.width - 350, canvas.height - 60);
|
ctx.fillText("KC-" + id, canvas.width/2, canvas.height - 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function wrapText(text, x, y, maxWidth, lineHeight) {
|
function wrapText(text, x, y, maxWidth, lineHeight) {
|
||||||
const words = text.split(" ");
|
const lines = text.split('\n');
|
||||||
let line = "";
|
for (let i = 0; i < lines.length; i++) {
|
||||||
for (let n = 0; n < words.length; n++) {
|
let words = lines[i].split(" ");
|
||||||
const testLine = line + words[n] + " ";
|
let line = "";
|
||||||
const metrics = ctx.measureText(testLine);
|
for (let n = 0; n < words.length; n++) {
|
||||||
const testWidth = metrics.width;
|
const testLine = line + words[n] + " ";
|
||||||
if (testWidth > maxWidth && n > 0) {
|
const metrics = ctx.measureText(testLine);
|
||||||
ctx.fillText(line, x, y);
|
const testWidth = metrics.width;
|
||||||
line = words[n] + " ";
|
if (testWidth > maxWidth && n > 0) {
|
||||||
y += lineHeight;
|
ctx.fillText(line, x, y);
|
||||||
} else {
|
line = words[n] + " ";
|
||||||
line = testLine;
|
y += lineHeight;
|
||||||
|
} else {
|
||||||
|
line = testLine;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
ctx.fillText(line, x, y);
|
||||||
|
y += lineHeight;
|
||||||
}
|
}
|
||||||
ctx.fillText(line, x, y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update card live when inputs change
|
// Update card live when inputs change
|
||||||
[nameInput, powerInput, charmInput, popularityInput, flavorInput,packInput].forEach(input => {
|
[nameInput, powerInput, charmInput, witInput, flavorInput,packInput,artistInput].forEach(input => {
|
||||||
input.addEventListener("input", drawCard);
|
input.addEventListener("input", drawCard);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Download button
|
// Download button
|
||||||
downloadBtn.addEventListener("click", () => {
|
downloadBtn.addEventListener("click", () => {
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.download = "kemoverse-card.png";
|
link.download = "kemoverse-card.webp";
|
||||||
link.href = canvas.toDataURL("image/png");
|
link.href = canvas.toDataURL("image/webp", 0.95);
|
||||||
link.click();
|
link.click();
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
Loading…
Add table
Reference in a new issue