// additional: fill with selected color as bucket from button? add a new button? but we already have fill BG (background only) // Also we can add a "flood fill active color" button. const floodFillActiveBtn = document.createElement('button'); // we inject it elegantly into tools panel, but i'll just add near export for extra tool, but we can include without overclutter: let's modify tools panel // get tools panel and insert new button const toolsDiv = document.querySelector('.tools-panel'); const fillActiveBtn = document.createElement('button'); fillActiveBtn.innerText = '🪣 FLOOD FILL (active)'; fillActiveBtn.className = 'btn'; fillActiveBtn.style.background = '#5a4a2e'; fillActiveBtn.addEventListener('click', () => // prompt for which color? we need a target pixel? simplest: fill whole canvas? no, flood fill requires seed. // We'll make it interactive: click on canvas after pressing flood mode? easier: show message. alert('🔮 Double-click on any pixel to flood fill with current color! (or use the FILL BG button for full background)'); ); toolsDiv.appendChild(fillActiveBtn); // but also update fill BG: keep original fill background using default bg. // override fillBgBtn to fill canvas with DEFAULT_BG fillBgBtn.onclick = () => fillAllWithColor(DEFAULT_BG); ; // clear canvas clearBtn.onclick = () => clearCanvas(); ; // export PNG (scaled up to show pixels) function exportAsPNG() // we can export current canvas exactly as is, but we might also scale for better preview? But it's fine. const link = document.createElement('a'); link.download = `melon_pixelart_$currentGridSizex$currentGridSize.png`; link.href = canvas.toDataURL('image/png'); link.click(); // export sprite data (JSON matrix colors, also ready for melon playground community) function exportJSON() const exportObj = meta: tool: "Melon Playground Pixel Art Maker", gridSize: currentGridSize, paletteHint: "each cell holds hex color" , pixels: pixelMatrix.map(row => [...row]) ; const jsonStr = JSON.stringify(exportObj, null, 2); // copy to clipboard navigator.clipboard.writeText(jsonStr).then(() => alert(`✅ Sprite data ($currentGridSizex$currentGridSize) copied as JSON!\nYou can share or import later.`); ).catch(() => alert("⚠️ Could not copy, but you can use PNG export instead."); ); // color picker preview sync function updateColorPreview() const newColor = colorPicker.value; colorPreview.style.background = newColor; colorPicker.addEventListener('input', updateColorPreview); updateColorPreview(); // grid change event gridSizeSelect.addEventListener('change', changeGridSize); // ---- Mouse / touch event binding ---- canvas.addEventListener('mousedown', handlePointerStart); window.addEventListener('mousemove', handlePointerMove); window.addEventListener('mouseup', handlePointerEnd); // touch events canvas.addEventListener('touchstart', handlePointerStart, passive: false); canvas.addEventListener('touchmove', handlePointerMove, passive: false); canvas.addEventListener('touchend', handlePointerEnd); canvas.addEventListener('contextmenu', disableContextMenu); canvas.addEventListener('dblclick', handleCanvasDoubleClick); // additional keyboard: hold E to erase? optional, but we can set alt/ctrl handled already. // final initialisation function init() currentGridSize = 32; gridSizeSelect.value = "32"; pixelMatrix = initMatrix(currentGridSize, DEFAULT_BG); resizeAndRedraw(); // export listeners exportPngBtn.addEventListener('click', exportAsPNG); exportJsonBtn.addEventListener('click', exportJSON); init(); )(); </script> </body> </html>
// resize canvas and redraw from matrix function resizeAndRedraw() const size = currentGridSize; // physical canvas resolution: 400x400 gives nice pixel blocks // We'll use 400x400 to have integer cell sizes: 400 / size must be integer. // But for 48x48, 400/48 = 8.33 not integer => we'll adjust canvas resolution dynamically to keep crisp pixels. // better approach: set canvas size to a multiple of grid size to avoid subpixel artifacts. // Let's set canvas width/height = gridSize * 10 (or gridSize * 12) .. but we need crisp squares. // I'll compute canvas size as gridSize * 12 -> max 48*12=576 still fine, min 16*12=192. // But user expects 32x32 cell size around 10px each => 320px. // For consistency, we set canvas.width = gridSize * 10 (max 480 for 48, still crisp). // But for 48, 480/48 = 10px exactly. For 24, 240px. Good. const pixelSize = 10; // each cell is exactly 10px const newCanvasWidth = size * pixelSize; const newCanvasHeight = size * pixelSize; canvas.width = newCanvasWidth; canvas.height = newCanvasHeight; canvas.style.width = `$newCanvasWidthpx`; canvas.style.height = `$newCanvasHeightpx`; cellW = pixelSize; cellH = pixelSize; // redraw full matrix drawFullMatrix();
.color-label font-weight: bold; color: #ffdd99; font-size: 0.85rem; pixel art maker for melon playground
// update single cell in matrix & canvas function setPixel(row, col, color)
select, input background: #0e111b; border: 1px solid #ffb347; color: #ffe6c7; padding: 6px 10px; border-radius: 28px; font-family: monospace; font-weight: bold; // additional: fill with selected color as bucket
/* export area */ .export-area display: flex; justify-content: center; gap: 12px; margin-top: 0.5rem; flex-wrap: wrap;
h1 margin: 0 0 0.3rem 0; font-size: 1.9rem; text-align: center; font-weight: 800; letter-spacing: 2px; background: linear-gradient(135deg, #FFE6B0, #FFB347); -webkit-background-clip: text; background-clip: text; color: transparent; text-shadow: 0 2px 3px rgba(0,0,0,0.2); const floodFillActiveBtn = document
// fill background (alias for clear with current bg but also set custom) function fillBackground() fillAllWithColor(DEFAULT_BG);