File size: 4,264 Bytes
213c234 34f0868 f444918 34f0868 f444918 213c234 d60484d 213c234 f444918 c6f0353 f444918 34f0868 c6f0353 213c234 d60484d f444918 d60484d 213c234 c6f0353 213c234 f444918 213c234 f444918 34f0868 213c234 34f0868 f444918 213c234 f444918 34f0868 f444918 213c234 c6f0353 213c234 f444918 c6f0353 f444918 213c234 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
import type { PicletInstance } from '$lib/db/schema';
import { embedPicletMetadata } from './picletMetadata';
/**
* Generates a shareable image of a piclet with embedded metadata
*/
export async function generateShareableImage(piclet: PicletInstance): Promise<Blob> {
// Create canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) throw new Error('Could not create canvas context');
// Set canvas size - narrower width to match content, tighter height
const canvasWidth = 700;
const canvasHeight = 700; // Square format, tighter
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// Fill with striped background like battle view
ctx.fillStyle = '#f8f9fa';
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
// Create striped pattern
const stripeHeight = 10;
ctx.fillStyle = 'rgba(76, 175, 80, 0.2)'; // Light green
for (let y = 0; y < canvasHeight; y += stripeHeight * 2) {
ctx.fillRect(0, y, canvasWidth, stripeHeight);
}
// Add alternating darker stripes
ctx.fillStyle = 'rgba(76, 175, 80, 0.1)'; // Lighter green
for (let y = stripeHeight; y < canvasHeight; y += stripeHeight * 2) {
ctx.fillRect(0, y, canvasWidth, stripeHeight);
}
// Load piclet image first
const picletImg = await loadImage(piclet.imageData || piclet.imageUrl);
const picletSize = 512;
const picletX = (canvasWidth - picletSize) / 2;
const picletY = 120; // Position piclet closer to top, leaving room for name
// Load and draw grass platform positioned under the piclet
const grassImg = await loadImage('/assets/grass.PNG');
const platformSize = 1024; // Slightly larger than piclet
const platformX = (canvasWidth - platformSize) / 2;
const platformY = picletY + 512;
ctx.drawImage(grassImg, platformX, platformY, platformSize, platformSize);
// Draw piclet on top of platform
ctx.drawImage(picletImg, picletX, picletY, picletSize, picletSize);
// Add piclet name with video game font
ctx.fillStyle = 'white';
ctx.strokeStyle = '#1e3a8a'; // Dark blue outline
ctx.lineWidth = 8;
// Try to use a more gaming-style font, fallback to Impact
ctx.font = 'bold 64px "Press Start 2P", "Courier New", Impact, monospace';
ctx.textAlign = 'center';
ctx.shadowColor = 'rgba(0, 0, 0, 0.8)';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 4;
ctx.shadowOffsetY = 4;
const nameText = piclet.nickname || piclet.typeId;
// Draw name with outline and shadow
ctx.strokeText(nameText, canvasWidth / 2, 80);
ctx.fillText(nameText, canvasWidth / 2, 80);
// Reset shadow
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// Load and draw translucent watermark
const logoImg = await loadImage('/assets/snap_logo.png');
const logoSize = 120;
ctx.globalAlpha = 0.3; // More translucent
ctx.drawImage(logoImg, canvasWidth - logoSize - 20, canvasHeight - logoSize - 20, logoSize, logoSize);
ctx.globalAlpha = 1.0;
// Get the image as blob
const blob = await canvasToBlob(canvas);
// Embed metadata in the blob
return embedPicletMetadata(blob, piclet);
}
/**
* Downloads a piclet card image
*/
export async function downloadPicletCard(piclet: PicletInstance, filename?: string): Promise<void> {
const blob = await generateShareableImage(piclet);
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename || `Piclet_${piclet.nickname || piclet.typeId}_Lv${piclet.level}.png`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
/**
* Helper to load an image
*/
function loadImage(src: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => resolve(img);
img.onerror = reject;
img.src = src;
});
}
/**
* Convert canvas to blob
*/
function canvasToBlob(canvas: HTMLCanvasElement): Promise<Blob> {
return new Promise((resolve, reject) => {
canvas.toBlob((blob) => {
if (blob) resolve(blob);
else reject(new Error('Failed to create blob'));
}, 'image/png');
});
}
|