about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2024-12-24 19:32:38 -0500
committerelioat <elioat@tilde.institute>2024-12-24 19:32:38 -0500
commit9ec48fa5f463fcae13c518f978a91e2bf5942479 (patch)
tree4b57dfd39b67ce800b4b9890576c716f59bb6089
parentc14408c56bdae4977392c0a4c94482fecf50e80a (diff)
downloadtour-9ec48fa5f463fcae13c518f978a91e2bf5942479.tar.gz
*
-rw-r--r--js/pixel-art/pixel/app.js215
-rw-r--r--js/pixel-art/pixel/index.html1
2 files changed, 150 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
diff --git a/js/pixel-art/pixel/index.html b/js/pixel-art/pixel/index.html
index d4b690d..74c0216 100644
--- a/js/pixel-art/pixel/index.html
+++ b/js/pixel-art/pixel/index.html
@@ -178,6 +178,7 @@
             <button id="panDownBtn">⬇️</button>
         </div>
         <button id="centerViewBtn">🎯 Center View</button>
+        <button id="newCanvasBtn">➕ New Canvas</button>
         <hr>
         <button id="exportBtn">Export as PNG</button>
         <button id="resetBtn">Reset</button>