|
import type { PicletInstance } from '$lib/db/schema'; |
|
import { embedPicletMetadata } from './picletMetadata'; |
|
|
|
|
|
|
|
|
|
export async function generateShareableImage(piclet: PicletInstance): Promise<Blob> { |
|
|
|
const canvas = document.createElement('canvas'); |
|
const ctx = canvas.getContext('2d'); |
|
if (!ctx) throw new Error('Could not create canvas context'); |
|
|
|
|
|
const canvasWidth = 700; |
|
const canvasHeight = 700; |
|
canvas.width = canvasWidth; |
|
canvas.height = canvasHeight; |
|
|
|
|
|
ctx.fillStyle = '#f8f9fa'; |
|
ctx.fillRect(0, 0, canvasWidth, canvasHeight); |
|
|
|
|
|
const stripeHeight = 10; |
|
ctx.fillStyle = 'rgba(76, 175, 80, 0.2)'; |
|
for (let y = 0; y < canvasHeight; y += stripeHeight * 2) { |
|
ctx.fillRect(0, y, canvasWidth, stripeHeight); |
|
} |
|
|
|
|
|
ctx.fillStyle = 'rgba(76, 175, 80, 0.1)'; |
|
for (let y = stripeHeight; y < canvasHeight; y += stripeHeight * 2) { |
|
ctx.fillRect(0, y, canvasWidth, stripeHeight); |
|
} |
|
|
|
|
|
const picletImg = await loadImage(piclet.imageData || piclet.imageUrl); |
|
const picletSize = 512; |
|
const picletX = (canvasWidth - picletSize) / 2; |
|
const picletY = 120; |
|
|
|
|
|
const grassImg = await loadImage('/assets/grass.PNG'); |
|
const platformSize = 1024; |
|
const platformX = (canvasWidth - platformSize) / 2; |
|
const platformY = picletY + 512; |
|
ctx.drawImage(grassImg, platformX, platformY, platformSize, platformSize); |
|
|
|
|
|
ctx.drawImage(picletImg, picletX, picletY, picletSize, picletSize); |
|
|
|
|
|
ctx.fillStyle = 'white'; |
|
ctx.strokeStyle = '#1e3a8a'; |
|
ctx.lineWidth = 8; |
|
|
|
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; |
|
|
|
|
|
ctx.strokeText(nameText, canvasWidth / 2, 80); |
|
ctx.fillText(nameText, canvasWidth / 2, 80); |
|
|
|
|
|
ctx.shadowColor = 'transparent'; |
|
ctx.shadowBlur = 0; |
|
ctx.shadowOffsetX = 0; |
|
ctx.shadowOffsetY = 0; |
|
|
|
|
|
const logoImg = await loadImage('/assets/snap_logo.png'); |
|
const logoSize = 120; |
|
ctx.globalAlpha = 0.3; |
|
ctx.drawImage(logoImg, canvasWidth - logoSize - 20, canvasHeight - logoSize - 20, logoSize, logoSize); |
|
ctx.globalAlpha = 1.0; |
|
|
|
|
|
const blob = await canvasToBlob(canvas); |
|
|
|
|
|
return embedPicletMetadata(blob, piclet); |
|
} |
|
|
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
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; |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
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'); |
|
}); |
|
} |
|
|
|
|