diff options
Diffstat (limited to 'js/pixel-art/pixel/app.js')
-rw-r--r-- | js/pixel-art/pixel/app.js | 215 |
1 files changed, 149 insertions, 66 deletions
diff --git a/js/pixel-art/pixel/app.js b/js/pixel-art/pixel/app.js index d3a0d28..8fae18b 100644 --- a/js/pixel-art/pixel/app.js +++ b/js/pixel-art/pixel/app.js @@ -19,6 +19,10 @@ let lastY = null; let lastCell = null; const MIN_CELL_SIZE = 4; const MAX_CELL_SIZE = 64; +let canvases = []; +let currentCanvasIndex = 0; +let globalOffsetX = 0; +let globalOffsetY = 0; // Event Listeners canvas.addEventListener('mousedown', handleInputStart); @@ -41,10 +45,35 @@ document.getElementById('panDownBtn').addEventListener('click', () => handlePanB document.getElementById('panLeftBtn').addEventListener('click', () => handlePanButton('left')); document.getElementById('panRightBtn').addEventListener('click', () => handlePanButton('right')); document.getElementById('centerViewBtn').addEventListener('click', resetView); +document.getElementById('newCanvasBtn').addEventListener('click', addNewCanvas); resizeCanvas(); loadFromLocalStorage(); +function initializeApp() { + addNewCanvas(); + resizeCanvas(); + loadFromLocalStorage(); +} + +function addNewCanvas() { + canvases.push({ + grid: Array(gridWidth).fill().map(() => Array(gridHeight).fill(null)), + offsetX: 0, + offsetY: 0 + }); + currentCanvasIndex = canvases.length - 1; + centerGrid(); + drawGrid(); + saveToLocalStorage(); + + // Disable width/height inputs after first canvas is created + if (canvases.length > 0) { + document.getElementById('gridWidth').disabled = true; + document.getElementById('gridHeight').disabled = true; + } +} + function initializeGrid() { grid = Array(gridWidth).fill().map(() => Array(gridHeight).fill(null)); } @@ -57,38 +86,53 @@ function resizeCanvas() { } function centerGrid() { - offsetX = Math.max((canvas.width - (gridWidth * cellSize)) / 2, 0); - offsetY = Math.max((canvas.height - (gridHeight * cellSize)) / 2, 0); + if (canvases.length === 0) return; + + canvases.forEach((canvasData, index) => { + canvasData.offsetY = Math.max((canvas.height - (gridHeight * cellSize)) / 2, 0); + if (index === 0) { + canvasData.offsetX = Math.max((canvas.width - (gridWidth * cellSize * canvases.length) - (cellSize * (canvases.length - 1))) / 2, 0); + } else { + // Position each canvas just one cell width apart + const previousCanvas = canvases[index - 1]; + canvasData.offsetX = previousCanvas.offsetX + (gridWidth * cellSize) + cellSize; + } + }); } function drawGrid() { ctx.fillStyle = 'teal'; ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.strokeStyle = '#888888'; - for (let x = 0; x < gridWidth; x++) { - for (let y = 0; y < gridHeight; y++) { - const cellX = x * cellSize + offsetX; - const cellY = y * cellSize + offsetY; - - // Fill cell background - ctx.fillStyle = grid[x][y] || '#f7f7f7'; - ctx.fillRect(cellX, cellY, cellSize, cellSize); - - // Draw cell border - ctx.strokeRect(cellX, cellY, cellSize, cellSize); - - // Draw diagonal line for empty cells - if (!grid[x][y]) { - ctx.beginPath(); - ctx.strokeStyle = '#bfbfbf'; - ctx.moveTo(cellX, cellY); - ctx.lineTo(cellX + cellSize, cellY + cellSize); - ctx.stroke(); - ctx.strokeStyle = '#888888'; // Reset stroke style for borders + // Draw all canvases + canvases.forEach((canvasData, index) => { + const xOffset = canvasData.offsetX + globalOffsetX; + + for (let x = 0; x < gridWidth; x++) { + for (let y = 0; y < gridHeight; y++) { + const cellX = x * cellSize + xOffset; + const cellY = y * cellSize + canvasData.offsetY + globalOffsetY; + + // Fill cell background + ctx.fillStyle = canvasData.grid[x][y] || '#f7f7f7'; + ctx.fillRect(cellX, cellY, cellSize, cellSize); + + // Draw cell border + ctx.strokeStyle = '#888888'; + ctx.strokeRect(cellX, cellY, cellSize, cellSize); + + // Draw diagonal line for empty cells + if (!canvasData.grid[x][y]) { + ctx.beginPath(); + ctx.strokeStyle = '#bfbfbf'; + ctx.moveTo(cellX, cellY); + ctx.lineTo(cellX + cellSize, cellY + cellSize); + ctx.stroke(); + ctx.strokeStyle = '#888888'; + } } } - } + }); } function addToColorHistory(color) { @@ -130,18 +174,24 @@ function handleReset() { localStorage.removeItem('pixelArtConfig'); colorHistory = []; renderColorHistory(); + canvases = []; // Clear canvases array document.getElementById('gridWidth').value = gridWidth; document.getElementById('gridHeight').value = gridHeight; + // Re-enable inputs after reset + document.getElementById('gridWidth').disabled = false; + document.getElementById('gridHeight').disabled = false; alert("Grid reset, color history cleared, and local storage cleared."); } } function handlePan(e) { const step = cellSize; - if (e.key === 'ArrowUp') offsetY += step; - if (e.key === 'ArrowDown') offsetY -= step; - if (e.key === 'ArrowLeft') offsetX += step; - if (e.key === 'ArrowRight') offsetX -= step; + if (canvases.length === 0) return; + + if (e.key === 'ArrowUp') globalOffsetY += step; + if (e.key === 'ArrowDown') globalOffsetY -= step; + if (e.key === 'ArrowLeft') globalOffsetX += step; + if (e.key === 'ArrowRight') globalOffsetX -= step; drawGrid(); } @@ -156,13 +206,15 @@ function updateGridSize() { function saveToLocalStorage() { const gridData = { - gridWidth: gridWidth, - gridHeight: gridHeight, - cellSize: cellSize, - colorHistory: colorHistory, - currentColor: currentColor, - grid: grid, - isPaletteVisible: isPaletteVisible + gridWidth, + gridHeight, + cellSize, + colorHistory, + currentColor, + canvases, + isPaletteVisible, + globalOffsetX, + globalOffsetY }; localStorage.setItem('pixelArtConfig', JSON.stringify(gridData)); } @@ -176,7 +228,18 @@ function loadFromLocalStorage() { cellSize = gridData.cellSize || 16; colorHistory = gridData.colorHistory || []; currentColor = gridData.currentColor || '#000000'; - grid = gridData.grid || Array(gridWidth).fill().map(() => Array(gridHeight).fill(null)); + canvases = gridData.canvases || [{ + grid: Array(gridWidth).fill().map(() => Array(gridHeight).fill(null)), + offsetX: 0, + offsetY: 0 + }]; + + // Disable inputs if there are canvases + if (canvases.length > 0) { + document.getElementById('gridWidth').disabled = true; + document.getElementById('gridHeight').disabled = true; + } + document.getElementById('gridWidth').value = gridWidth; document.getElementById('gridHeight').value = gridHeight; document.getElementById('colorPicker').value = currentColor; @@ -188,6 +251,8 @@ function loadFromLocalStorage() { paletteToggle.classList.add('hidden'); paletteToggle.innerHTML = '🎨'; } + globalOffsetX = gridData.globalOffsetX || 0; + globalOffsetY = gridData.globalOffsetY || 0; } else { initializeGrid(); centerGrid(); @@ -196,23 +261,25 @@ function loadFromLocalStorage() { } function exportToPNG() { - const tempCanvas = document.createElement('canvas'); - const tempCtx = tempCanvas.getContext('2d'); - tempCanvas.width = gridWidth * cellSize; - tempCanvas.height = gridHeight * cellSize; + canvases.forEach((canvasData, index) => { + const tempCanvas = document.createElement('canvas'); + const tempCtx = tempCanvas.getContext('2d'); + tempCanvas.width = gridWidth * cellSize; + tempCanvas.height = gridHeight * cellSize; - for (let x = 0; x < gridWidth; x++) { - for (let y = 0; y < gridHeight; y++) { - tempCtx.fillStyle = grid[x][y] || 'transparent'; - tempCtx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize); + for (let x = 0; x < gridWidth; x++) { + for (let y = 0; y < gridHeight; y++) { + tempCtx.fillStyle = canvasData.grid[x][y] || 'transparent'; + tempCtx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize); + } } - } - tempCanvas.toBlob(blob => { - const link = document.createElement('a'); - link.href = URL.createObjectURL(blob); - link.download = 'pixel-art.png'; - link.click(); + tempCanvas.toBlob(blob => { + const link = document.createElement('a'); + link.href = URL.createObjectURL(blob); + link.download = `pixel-art-${index + 1}.png`; + link.click(); + }); }); } @@ -224,11 +291,11 @@ function handleInputStart(e) { if (cell) { lastCell = cell; - // If cell already has a color, remove it. Otherwise, add color - if (grid[cell.x][cell.y]) { - grid[cell.x][cell.y] = null; + currentCanvasIndex = cell.canvasIndex; + if (canvases[currentCanvasIndex].grid[cell.x][cell.y]) { + canvases[currentCanvasIndex].grid[cell.x][cell.y] = null; } else { - grid[cell.x][cell.y] = currentColor; + canvases[currentCanvasIndex].grid[cell.x][cell.y] = currentColor; } drawGrid(); saveToLocalStorage(); @@ -245,7 +312,7 @@ function handleInputMove(e) { if (cell && (!lastCell || cell.x !== lastCell.x || cell.y !== lastCell.y)) { lastCell = cell; // When dragging, always draw (don't erase) - grid[cell.x][cell.y] = currentColor; + canvases[currentCanvasIndex].grid[cell.x][cell.y] = currentColor; drawGrid(); saveToLocalStorage(); } @@ -275,11 +342,21 @@ function getInputCoordinates(e) { } function getCellFromCoords(coords) { - const x = Math.floor((coords.x - offsetX) / cellSize); - const y = Math.floor((coords.y - offsetY) / cellSize); + if (canvases.length === 0) return null; - if (x >= 0 && x < gridWidth && y >= 0 && y < gridHeight) { - return { x, y }; + for (let i = 0; i < canvases.length; i++) { + const canvasData = canvases[i]; + const canvasLeft = canvasData.offsetX + globalOffsetX; + const canvasRight = canvasLeft + (gridWidth * cellSize); + + if (coords.x >= canvasLeft && coords.x < canvasRight) { + const x = Math.floor((coords.x - canvasLeft) / cellSize); + const y = Math.floor((coords.y - (canvasData.offsetY + globalOffsetY)) / cellSize); + + if (x >= 0 && x < gridWidth && y >= 0 && y < gridHeight) { + return { x, y, canvasIndex: i }; + } + } } return null; } @@ -315,19 +392,21 @@ function handleZoom(factor) { } function handlePanButton(direction) { + if (canvases.length === 0) return; + const step = cellSize; switch(direction) { case 'up': - offsetY += step; + globalOffsetY += step; break; case 'down': - offsetY -= step; + globalOffsetY -= step; break; case 'left': - offsetX += step; + globalOffsetX += step; break; case 'right': - offsetX -= step; + globalOffsetX -= step; break; } drawGrid(); @@ -335,7 +414,11 @@ function handlePanButton(direction) { function resetView() { cellSize = 16; // Reset to default zoom - centerGrid(); - drawGrid(); - saveToLocalStorage(); + globalOffsetX = 0; + globalOffsetY = 0; + if (canvases.length > 0) { + centerGrid(); + drawGrid(); + saveToLocalStorage(); + } } \ No newline at end of file |