File size: 4,538 Bytes
213c234 deb38a3 f444918 213c234 d60484d 213c234 f444918 c6f0353 deb38a3 c6f0353 213c234 deb38a3 f444918 deb38a3 213c234 c6f0353 213c234 deb38a3 213c234 deb38a3 213c234 deb38a3 213c234 deb38a3 f444918 213c234 c6f0353 213c234 deb38a3 c6f0353 deb38a3 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 133 134 135 136 137 138 139 140 141 142 |
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 - full piclet size plus padding for name
const canvasWidth = 1024;
const canvasHeight = 1200; // Extra height for name
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 = 1024; // Full size!
const picletX = 0;
const picletY = 150; // Leave room for name at top
// Load and draw grass platform positioned under the piclet
const grassImg = await loadImage('/assets/grass.PNG');
const platformSize = 1200; // Larger than piclet for proper positioning
const platformX = (canvasWidth - platformSize) / 2;
const platformY = picletY + picletSize - 300; // Platform under piclet
ctx.drawImage(grassImg, platformX, platformY, platformSize, platformSize);
// Draw piclet on top of platform
ctx.drawImage(picletImg, picletX, picletY, picletSize, picletSize);
// Add sleek modern text styling
const nameText = piclet.nickname || piclet.typeId;
// Create gradient for text
const gradient = ctx.createLinearGradient(0, 50, 0, 120);
gradient.addColorStop(0, '#ffffff');
gradient.addColorStop(1, '#e0e0e0');
// Modern sleek font with gradient and subtle shadow
ctx.font = 'bold 72px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// Subtle shadow for depth
ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 3;
// Dark outline for contrast
ctx.strokeStyle = '#2c3e50';
ctx.lineWidth = 4;
ctx.strokeText(nameText.toUpperCase(), canvasWidth / 2, 80);
// Fill with gradient
ctx.fillStyle = gradient;
ctx.fillText(nameText.toUpperCase(), 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 = 150;
ctx.globalAlpha = 0.3; // More translucent
ctx.drawImage(logoImg, canvasWidth - logoSize - 30, canvasHeight - logoSize - 30, 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');
});
}
|