// KVIST Poster Creator const canvas = document.getElementById('posterCanvas'); const ctx = canvas.getContext('2d'); // B4 dimensions (250mm x 353mm) - using proportional pixels const POSTER_WIDTH = 750; const POSTER_HEIGHT = 1059; canvas.width = POSTER_WIDTH; canvas.height = POSTER_HEIGHT; // State const state = { mainText: 'B4', noteText: 'Dag Byström; Selected Works 2024–2026.', fontSize: 200, position: 'center', gradientIndex: 0, mode: 'dark' }; // Gradients const gradients = [ { start: '#0f0f0f', end: '#1a1a2e' }, { start: '#667eea', end: '#764ba2' }, { start: '#f093fb', end: '#f5576c' }, { start: '#4facfe', end: '#00f2fe' }, { start: '#43e97b', end: '#38f9d7' }, { start: '#fa709a', end: '#fee140' }, { start: '#a18cd1', end: '#fbc2eb' }, { start: '#fccb90', end: '#d57eeb' }, { start: '#e0c3fc', end: '#8ec5fc' }, { start: '#f5af19', end: '#f12711' }, { start: '#c471f5', end: '#fa71cd' }, { start: '#48c6ef', end: '#6f86d6' } ]; // Draw poster function drawPoster() { const { mainText, noteText, fontSize, position, gradientIndex, mode } = state; const grad = gradients[gradientIndex]; // Background gradient const gradient = ctx.createLinearGradient(0, 0, POSTER_WIDTH, POSTER_HEIGHT); if (mode === 'dark') { gradient.addColorStop(0, grad.start); gradient.addColorStop(1, grad.end); } else { gradient.addColorStop(0, '#ffffff'); gradient.addColorStop(1, '#f0f0f0'); } ctx.fillStyle = gradient; ctx.fillRect(0, 0, POSTER_WIDTH, POSTER_HEIGHT); // Text color const textColor = mode === 'dark' ? '#ffffff' : '#000000'; const noteColor = mode === 'dark' ? 'rgba(255,255,255,0.6)' : 'rgba(0,0,0,0.6)'; // Calculate position const padding = 60; let x, y, textAlign, textBaseline; switch (position) { case 'top-left': x = padding; y = padding; textAlign = 'left'; textBaseline = 'top'; break; case 'top-center': x = POSTER_WIDTH / 2; y = padding; textAlign = 'center'; textBaseline = 'top'; break; case 'top-right': x = POSTER_WIDTH - padding; y = padding; textAlign = 'right'; textBaseline = 'top'; break; case 'center-left': x = padding; y = POSTER_HEIGHT / 2; textAlign = 'left'; textBaseline = 'middle'; break; case 'center': x = POSTER_WIDTH / 2; y = POSTER_HEIGHT / 2; textAlign = 'center'; textBaseline = 'middle'; break; case 'center-right': x = POSTER_WIDTH - padding; y = POSTER_HEIGHT / 2; textAlign = 'right'; textBaseline = 'middle'; break; case 'bottom-left': x = padding; y = POSTER_HEIGHT - padding; textAlign = 'left'; textBaseline = 'bottom'; break; case 'bottom-center': x = POSTER_WIDTH / 2; y = POSTER_HEIGHT - padding; textAlign = 'center'; textBaseline = 'bottom'; break; case 'bottom-right': x = POSTER_WIDTH - padding; y = POSTER_HEIGHT - padding; textAlign = 'right'; textBaseline = 'bottom'; break; default: x = POSTER_WIDTH / 2; y = POSTER_HEIGHT / 2; textAlign = 'center'; textBaseline = 'middle'; } // Draw main text with word wrapping ctx.font = `${fontSize}px Kvist`; ctx.fillStyle = textColor; ctx.textAlign = textAlign; ctx.textBaseline = textBaseline; // Word wrap logic const maxWidth = POSTER_WIDTH - padding * 2; const lines = wrapText(ctx, mainText, maxWidth); const lineHeight = fontSize * 1.1; const totalHeight = lines.length * lineHeight; // Adjust y for multi-line centering let startY = y; if (textBaseline === 'middle') { startY = y - totalHeight / 2 + lineHeight / 2; } else if (textBaseline === 'bottom') { startY = y - totalHeight + lineHeight; } lines.forEach((line, i) => { ctx.textBaseline = 'top'; ctx.fillText(line, x, startY + i * lineHeight); }); // Draw note text ctx.font = '14px -apple-system, BlinkMacSystemFont, sans-serif'; ctx.fillStyle = noteColor; ctx.textAlign = 'center'; ctx.textBaseline = 'bottom'; ctx.fillText(noteText, POSTER_WIDTH / 2, POSTER_HEIGHT - 30); } // Word wrap helper function wrapText(context, text, maxWidth) { const words = text.split(''); const lines = []; let currentLine = ''; for (let i = 0; i < words.length; i++) { const testLine = currentLine + words[i]; const metrics = context.measureText(testLine); if (metrics.width > maxWidth && currentLine.length > 0) { lines.push(currentLine); currentLine = words[i]; } else { currentLine = testLine; } } if (currentLine) { lines.push(currentLine); } return lines.length > 0 ? lines : ['']; } // Event Listeners document.getElementById('mainText').addEventListener('input', (e) => { state.mainText = e.target.value; drawPoster(); }); document.getElementById('noteText').addEventListener('input', (e) => { state.noteText = e.target.value; drawPoster(); }); document.getElementById('fontSize').addEventListener('input', (e) => { state.fontSize = parseInt(e.target.value); document.getElementById('fontSizeValue').textContent = `${state.fontSize}px`; drawPoster(); }); // Position buttons document.querySelectorAll('.pos-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.pos-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); state.position = btn.dataset.pos; drawPoster(); }); }); // Gradient buttons document.querySelectorAll('.grad-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.grad-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); state.gradientIndex = parseInt(btn.dataset.gradient); drawPoster(); }); }); // Mode buttons document.querySelectorAll('.mode-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); state.mode = btn.dataset.mode; drawPoster(); }); }); // Download document.getElementById('downloadBtn').addEventListener('click', () => { const link = document.createElement('a'); link.download = `KVIST-poster-${Date.now()}.png`; link.href = canvas.toDataURL('image/png'); link.click(); }); // Wait for font to load then draw document.fonts.ready.then(() => { drawPoster(); });Dag Byström — Selected Works 2024–2026 — PH subjekt page