diff options
-rw-r--r-- | js/pixel-art/pixel/app.js | 186 | ||||
-rw-r--r-- | js/pixel-art/pixel/index.html | 86 |
2 files changed, 272 insertions, 0 deletions
diff --git a/js/pixel-art/pixel/app.js b/js/pixel-art/pixel/app.js new file mode 100644 index 0000000..087801c --- /dev/null +++ b/js/pixel-art/pixel/app.js @@ -0,0 +1,186 @@ +const canvas = document.getElementById('canvas'); +const ctx = canvas.getContext('2d'); +const defaultGridWidth = 16; +const defaultGridHeight = 16; +let gridWidth = defaultGridWidth; +let gridHeight = defaultGridHeight; +let cellSize = 16; +let colorHistory = []; +let currentColor = '#000000'; +let grid = Array(gridWidth).fill().map(() => Array(gridHeight).fill(null)); +let offsetX = 0; +let offsetY = 0; + +// Event Listeners +canvas.addEventListener('click', handleCanvasClick); +document.getElementById('colorPicker').addEventListener('input', handleColorChange); +document.getElementById('gridWidth').addEventListener('change', updateGridSize); +document.getElementById('gridHeight').addEventListener('change', updateGridSize); +document.getElementById('resetBtn').addEventListener('click', handleReset); +document.getElementById('exportBtn').addEventListener('click', exportToPNG); +window.addEventListener('keydown', handlePan); + +// Initialization +resizeCanvas(); +loadFromLocalStorage(); + +// Functions +function initializeGrid() { + grid = Array(gridWidth).fill().map(() => Array(gridHeight).fill(null)); +} + +function resizeCanvas() { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + centerGrid(); + drawGrid(); +} + +function centerGrid() { + offsetX = Math.max((canvas.width - (gridWidth * cellSize)) / 2, 0); + offsetY = Math.max((canvas.height - (gridHeight * cellSize)) / 2, 0); +} + +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++) { + ctx.fillStyle = grid[x][y] || '#f7f7f7'; + ctx.fillRect(x * cellSize + offsetX, y * cellSize + offsetY, cellSize, cellSize); + ctx.strokeRect(x * cellSize + offsetX, y * cellSize + offsetY, cellSize, cellSize); + } + } +} + +function addToColorHistory(color) { + if (colorHistory.includes(color)) return; + if (colorHistory.length >= 10) colorHistory.shift(); + colorHistory.push(color); + renderColorHistory(); +} + +function renderColorHistory() { + const historyDiv = document.getElementById('colorHistory'); + historyDiv.innerHTML = ''; + colorHistory.forEach(color => { + const colorDiv = document.createElement('div'); + colorDiv.style.backgroundColor = color; + colorDiv.addEventListener('click', () => { + currentColor = color; + document.getElementById('colorPicker').value = color; + }); + historyDiv.appendChild(colorDiv); + }); +} + +function handleColorChange() { + currentColor = document.getElementById('colorPicker').value; + addToColorHistory(currentColor); + saveToLocalStorage(); +} + +function handleReset() { + gridWidth = defaultGridWidth; + gridHeight = defaultGridHeight; + initializeGrid(); + centerGrid(); + drawGrid(); + localStorage.removeItem('pixelArtConfig'); + colorHistory = []; + renderColorHistory(); + document.getElementById('gridWidth').value = gridWidth; + document.getElementById('gridHeight').value = gridHeight; + 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; + drawGrid(); +} + +function updateGridSize() { + gridWidth = parseInt(document.getElementById('gridWidth').value); + gridHeight = parseInt(document.getElementById('gridHeight').value); + initializeGrid(); + centerGrid(); + drawGrid(); + saveToLocalStorage(); +} + +function saveToLocalStorage() { + const gridData = { + gridWidth: gridWidth, + gridHeight: gridHeight, + cellSize: cellSize, + colorHistory: colorHistory, + currentColor: currentColor, + grid: grid, + }; + localStorage.setItem('pixelArtConfig', JSON.stringify(gridData)); +} + +function loadFromLocalStorage() { + const savedData = localStorage.getItem('pixelArtConfig'); + if (savedData) { + const gridData = JSON.parse(savedData); + gridWidth = gridData.gridWidth || 10; + gridHeight = gridData.gridHeight || 10; + cellSize = gridData.cellSize || 16; + colorHistory = gridData.colorHistory || []; + currentColor = gridData.currentColor || '#000000'; + grid = gridData.grid || Array(gridWidth).fill().map(() => Array(gridHeight).fill(null)); + document.getElementById('gridWidth').value = gridWidth; + document.getElementById('gridHeight').value = gridHeight; + document.getElementById('colorPicker').value = currentColor; + centerGrid(); + drawGrid(); + } else { + initializeGrid(); + centerGrid(); + drawGrid(); + } +} + +function exportToPNG() { + 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); + } + } + + tempCanvas.toBlob(blob => { + const link = document.createElement('a'); + link.href = URL.createObjectURL(blob); + link.download = 'pixel-art.png'; + link.click(); + }); +} + +function handleCanvasClick(e) { + const rect = canvas.getBoundingClientRect(); + const x = Math.floor((e.clientX - rect.left - offsetX) / cellSize); + const y = Math.floor((e.clientY - rect.top - offsetY) / cellSize); + + if (x >= 0 && x < gridWidth && y >= 0 && y < gridHeight) { + if (e.detail === 2) { // Double-click resets the cell + grid[x][y] = null; + } else { // Single-click paints the cell with the current color + grid[x][y] = currentColor; + } + drawGrid(); + saveToLocalStorage(); + } +} \ No newline at end of file diff --git a/js/pixel-art/pixel/index.html b/js/pixel-art/pixel/index.html new file mode 100644 index 0000000..91f1813 --- /dev/null +++ b/js/pixel-art/pixel/index.html @@ -0,0 +1,86 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Pixel Pixel Pixel Pixel Pixel Pixel</title> + <meta name="description" content="Draw me a goblin, please."> + <style> + body, html { + margin: 0; + padding: 0; + overflow: hidden; + background-color: teal; + } + button { + padding: 4px 10px; + font-size: 16px; + cursor: pointer; + } + #canvas { + display: block; + } + #palette { + position: absolute; + top: 10px; + right: 10px; + background-color: beige; + padding: 10px; + border: 1px solid #ccc; + border-radius: 5px; + width: 150px; + } + + #palette label, + #palette input, + #palette button { + display: block; + margin-bottom: 10px; + width: 100%; + } + + .color-history { + display: flex; + flex-wrap: wrap; + } + + .color-history div { + width: 20px; + height: 20px; + border: 1px solid #000; + cursor: pointer; + margin-right: 5px; + margin-bottom: 5px; + } + + .color-history { + display: flex; + margin-top: 10px; + } + .color-history div { + width: 20px; + height: 20px; + border: 1px solid #000; + cursor: pointer; + margin-right: 5px; + } + </style> +</head> +<body> + <canvas id="canvas"></canvas> + <div id="palette"> + <label for="gridWidth">Grid Width:</label> + <input type="number" id="gridWidth" value="16" min="1"> + <label for="gridHeight">Grid Height:</label> + <input type="number" id="gridHeight" value="16" min="1"> + <br> + <label for="colorPicker">Select Color:</label> + <input type="color" id="colorPicker" value="#000000"> + <div id="colorHistory" class="color-history"></div> + <button id="exportBtn">Export as PNG</button> + <button id="resetBtn">Reset</button> + <!--<input type="file" id="importBtn" accept="image/png">--> + </div> + <script src="app.js"></script> +</body> +</html> |