about summary refs log blame commit diff stats
path: root/js/pixel-art/pixel/app.js
blob: 3e37db88a3efc41a905db73cbb2f8e2b509eadef (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                                           


                                                                



                      

                         

                  





                                                        





                                                                                    
                                                       

                                                                                         



                                                                                                 
                                                                              
 


                       

























































                                                                                               














                                                                                                               



























                                                                       
                                          


















                                                                                                





                                                             



























                                                                             




                                           
    






                                                                       



                             

 
















































                                                                                











                                                 






                                                                                            

                                                                






                           


















                                     






                                           
 
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;
const paletteToggle = document.getElementById('palette-toggle');
const palette = document.getElementById('palette');
let isPaletteVisible = true;
let isDrawing = false;
let lastX = null;
let lastY = null;
let lastCell = null;
const MIN_CELL_SIZE = 4;
const MAX_CELL_SIZE = 64;

// Event Listeners
canvas.addEventListener('mousedown', handleInputStart);
canvas.addEventListener('mousemove', handleInputMove);
canvas.addEventListener('mouseup', handleInputEnd);
canvas.addEventListener('touchstart', handleInputStart);
canvas.addEventListener('touchmove', handleInputMove);
canvas.addEventListener('touchend', handleInputEnd);
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);
paletteToggle.addEventListener('click', togglePalette);
document.getElementById('zoomInBtn').addEventListener('click', () => handleZoom(1.5));
document.getElementById('zoomOutBtn').addEventListener('click', () => handleZoom(0.666));
document.getElementById('panUpBtn').addEventListener('click', () => handlePanButton('up'));
document.getElementById('panDownBtn').addEventListener('click', () => handlePanButton('down'));
document.getElementById('panLeftBtn').addEventListener('click', () => handlePanButton('left'));
document.getElementById('panRightBtn').addEventListener('click', () => handlePanButton('right'));
document.getElementById('centerViewBtn').addEventListener('click', resetView);

resizeCanvas();
loadFromLocalStorage();

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() {
    const confirmReset = confirm("Are you sure you want to reset? This will clear your drawing and settings.");
    
    if (confirmReset) {
        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,
        isPaletteVisible: isPaletteVisible
    };
    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();
        isPaletteVisible = gridData.isPaletteVisible ?? true;
        if (!isPaletteVisible) {
            palette.classList.add('hidden');
            paletteToggle.classList.add('hidden');
            paletteToggle.innerHTML = '🎨';
        }
    } 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 handleInputStart(e) {
    e.preventDefault();
    isDrawing = true;
    const coords = getInputCoordinates(e);
    const cell = getCellFromCoords(coords);
    
    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;
        } else {
            grid[cell.x][cell.y] = currentColor;
        }
        drawGrid();
        saveToLocalStorage();
    }
}

function handleInputMove(e) {
    e.preventDefault();
    if (!isDrawing) return;
    
    const coords = getInputCoordinates(e);
    const cell = getCellFromCoords(coords);
    
    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;
        drawGrid();
        saveToLocalStorage();
    }
}

function handleInputEnd(e) {
    e.preventDefault();
    isDrawing = false;
    lastCell = null;
}

function getInputCoordinates(e) {
    const rect = canvas.getBoundingClientRect();
    if (e.touches) {
        // Touch event
        return {
            x: e.touches[0].clientX - rect.left,
            y: e.touches[0].clientY - rect.top
        };
    } else {
        // Mouse event
        return {
            x: e.clientX - rect.left,
            y: e.clientY - rect.top
        };
    }
}

function getCellFromCoords(coords) {
    const x = Math.floor((coords.x - offsetX) / cellSize);
    const y = Math.floor((coords.y - offsetY) / cellSize);
    
    if (x >= 0 && x < gridWidth && y >= 0 && y < gridHeight) {
        return { x, y };
    }
    return null;
}

function togglePalette() {
    isPaletteVisible = !isPaletteVisible;
    
    if (isPaletteVisible) {
        palette.classList.remove('hidden');
        paletteToggle.classList.remove('hidden');
        paletteToggle.innerHTML = '☰';
    } else {
        palette.classList.add('hidden');
        paletteToggle.classList.add('hidden');
        paletteToggle.innerHTML = '🎨';
    }
}

function handleZoom(factor) {
    const newCellSize = Math.max(MIN_CELL_SIZE, Math.min(MAX_CELL_SIZE, cellSize * factor));
    
    if (newCellSize === cellSize) return;
    
    // const centerX = (canvas.width / 2 - offsetX) / cellSize;
    // const centerY = (canvas.height / 2 - offsetY) / cellSize;
    
    cellSize = newCellSize;
    
    centerGrid();
    
    drawGrid();
    saveToLocalStorage();
}

function handlePanButton(direction) {
    const step = cellSize;
    switch(direction) {
        case 'up':
            offsetY += step;
            break;
        case 'down':
            offsetY -= step;
            break;
        case 'left':
            offsetX += step;
            break;
        case 'right':
            offsetX -= step;
            break;
    }
    drawGrid();
}

function resetView() {
    cellSize = 16; // Reset to default zoom
    centerGrid();
    drawGrid();
    saveToLocalStorage();
}