// PixelForge Studio - Main JavaScript // Canvas and context let canvas = document.getElementById('photo-canvas'); let ctx = canvas.getContext('2d'); let isDrawing = false; let currentTool = 'brush'; let currentColor = '#000000'; let brushSize = 5; let zoomLevel = 1; let history = []; let historyStep = -1; let layers = []; let activeLayer = 0; // Initialize canvas with white background function initCanvas() { ctx.fillStyle = 'white'; ctx.fillRect(0, 0, canvas.width, canvas.height); saveHistory(); } // Tool selection document.querySelectorAll('.tool-btn').forEach(btn => { btn.addEventListener('click', function() { document.querySelectorAll('.tool-btn').forEach(b => b.classList.remove('active')); this.classList.add('active'); currentTool = this.dataset.tool; updateCursor(); }); }); // Update cursor based on tool function updateCursor() { switch(currentTool) { case 'brush': case 'eraser': canvas.style.cursor = 'crosshair'; break; case 'move': canvas.style.cursor = 'move'; break; case 'eyedropper': canvas.style.cursor = 'copy'; break; case 'text': canvas.style.cursor = 'text'; break; case 'zoom': canvas.style.cursor = 'zoom-in'; break; default: canvas.style.cursor = 'default'; } } // Drawing functions canvas.addEventListener('mousedown', startDrawing); canvas.addEventListener('mousemove', draw); canvas.addEventListener('mouseup', stopDrawing); canvas.addEventListener('mouseout', stopDrawing); function startDrawing(e) { if (currentTool === 'brush' || currentTool === 'eraser') { isDrawing = true; const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; ctx.beginPath(); ctx.moveTo(x, y); } } function draw(e) { if (!isDrawing) return; const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; if (currentTool === 'brush') { ctx.globalCompositeOperation = 'source-over'; ctx.strokeStyle = currentColor; ctx.lineWidth = brushSize; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.lineTo(x, y); ctx.stroke(); ctx.beginPath(); ctx.moveTo(x, y); } else if (currentTool === 'eraser') { ctx.globalCompositeOperation = 'destination-out'; ctx.lineWidth = brushSize * 2; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.lineTo(x, y); ctx.stroke(); ctx.beginPath(); ctx.moveTo(x, y); } updateCursorPosition(e); } function stopDrawing() { if (isDrawing) { isDrawing = false; ctx.beginPath(); saveHistory(); } } // Color picker functionality let rSlider = document.getElementById('color-r'); let gSlider = document.getElementById('color-g'); let bSlider = document.getElementById('color-b'); let rVal = document.getElementById('r-val'); let gVal = document.getElementById('g-val'); let bVal = document.getElementById('b-val'); let fgColor = document.getElementById('fg-color'); function updateColor() { const r = rSlider.value; const g = gSlider.value; const b = bSlider.value; currentColor = `rgb(${r}, ${g}, ${b})`; fgColor.style.backgroundColor = currentColor; rVal.textContent = r; gVal.textContent = g; bVal.textContent = b; } rSlider.addEventListener('input', updateColor); gSlider.addEventListener('input', updateColor); bSlider.addEventListener('input', updateColor); // Opacity control let opacitySlider = document.getElementById('opacity'); let opacityVal = document.getElementById('opacity-val'); opacitySlider.addEventListener('input', function() { ctx.globalAlpha = this.value / 100; opacityVal.textContent = this.value + '%'; }); // Zoom controls document.getElementById('zoom-in').addEventListener('click', function() { if (zoomLevel < 3) { zoomLevel += 0.25; updateZoom(); } }); document.getElementById('zoom-out').addEventListener('click', function() { if (zoomLevel > 0.25) { zoomLevel -= 0.25; updateZoom(); } }); function updateZoom() { let mainCanvas = document.getElementById('main-canvas'); mainCanvas.style.transform = `scale(${zoomLevel})`; document.getElementById('zoom-level').textContent = Math.round(zoomLevel * 100) + '%'; } // History management function saveHistory() { historyStep++; if (historyStep < history.length) { history.length = historyStep; } history.push(canvas.toDataURL()); } function undo() { if (historyStep > 0) { historyStep--; restoreCanvas(); } } function redo() { if (historyStep < history.length - 1) { historyStep++; restoreCanvas(); } } function restoreCanvas() { let img = new Image(); img.src = history[historyStep]; img.onload = function() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(img, 0, 0); }; } // Keyboard shortcuts document.addEventListener('keydown', function(e) { if (e.ctrlKey || e.metaKey) { switch(e.key) { case 'z': e.preventDefault(); undo(); break; case 'y': e.preventDefault(); redo(); break; case 's': e.preventDefault(); saveImage(); break; case 'o': e.preventDefault(); openImage(); break; case 'n': e.preventDefault(); document.getElementById('new-doc-modal').classList.remove('hidden'); break; } } // Tool shortcuts switch(e.key) { case 'b': document.querySelector('[data-tool="brush"]').click(); break; case 'e': document.querySelector('[data-tool="eraser"]').click(); break; case 'm': document.querySelector('[data-tool="move"]').click(); break; case 't': document.querySelector('[data-tool="text"]').click(); break; case 'c': document.querySelector('[data-tool="crop"]').click(); break; case 'i': document.querySelector('[data-tool="eyedropper"]').click(); break; case 'w': document.querySelector('[data-tool="wand"]').click(); break; case 'g': document.querySelector('[data-tool="gradient"]').click(); break; case 'z': if (!e.ctrlKey && !e.metaKey) { document.querySelector('[data-tool="zoom"]').click(); } break; } }); // Image save and load function saveImage() { const link = document.createElement('a'); link.download = 'pixelforge-image.png'; link.href = canvas.toDataURL(); link.click(); } function openImage() { const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.onchange = function(e) { const file = e.target.files[0]; const reader = new FileReader(); reader.onload = function(event) { const img = new Image(); img.onload = function() { canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); saveHistory(); }; img.src = event.target.result; }; reader.readAsDataURL(file); }; input.click(); } // Update cursor position function updateCursorPosition(e) { const rect = canvas.getBoundingClientRect(); const x = Math.round(e.clientX - rect.left); const y = Math.round(e.clientY - rect.top); document.getElementById('cursor-position').textContent = `X: ${x}, Y: ${y}`; } canvas.addEventListener('mousemove', updateCursorPosition); // Layer management function addLayer() { const layersList = document.getElementById('layers-list'); const layerCount = layersList.children.length; const layerItem = document.createElement('div'); layerItem.className = 'layer-item bg-gray-700 rounded p-2 mb-2 flex items-center space-x-2 cursor-pointer'; layerItem.innerHTML = `
Layer ${layerCount} 100% `; layersList.insertBefore(layerItem, layersList.firstChild); feather.replace(); // Add click handler for layer selection layerItem.addEventListener('click', function() { document.querySelectorAll('.layer-item').forEach(item => { item.classList.remove('active'); }); this.classList.add('active'); activeLayer = layerCount; }); } // Modal functions function closeModal() { document.getElementById('new-doc-modal').classList.add('hidden'); } // Fill slider document.getElementById('fill').addEventListener('input', function() { document.getElementById('fill-val').textContent = this.value + '%'; }); // Eyedropper tool canvas.addEventListener('click', function(e) { if (currentTool === 'eyedropper') { const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; const pixel = ctx.getImageData(x, y, 1, 1).data; rSlider.value = pixel[0]; gSlider.value = pixel[1]; bSlider.value = pixel[2]; updateColor(); } }); // Text tool canvas.addEventListener('click', function(e) { if (currentTool === 'text') { const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; const text = prompt('Enter text:'); if (text) { ctx.font = '20px Arial'; ctx.fillStyle = currentColor; ctx.fillText(text, x, y); saveHistory(); } } }); // Initialize initCanvas(); document.querySelector('[data-tool="brush"]').classList.add('active'); // Add initial layer layers.push({ name: 'Background', visible: true, opacity: 1 }); // Drag and drop support document.body.addEventListener('dragover', function(e) { e.preventDefault(); document.body.classList.add('dragover'); }); document.body.addEventListener('dragleave', function() { document.body.classList.remove('dragover'); }); document.body.addEventListener('drop', function(e) { e.preventDefault(); document.body.classList.remove('dragover'); const file = e.dataTransfer.files[0]; if (file && file.type.startsWith('image/')) { const reader = new FileReader(); reader.onload = function(event) { const img = new Image(); img.onload = function() { canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); saveHistory(); }; img.src = event.target.result; }; reader.readAsDataURL(file); } }); // Filter functions function applyBlur() { ctx.filter = 'blur(5px)'; const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); ctx.putImageData(imageData, 0, 0); ctx.filter = 'none'; saveHistory(); } function applyGrayscale() { const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { const gray = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114; data[i] = gray; data[i + 1] = gray; data[i + 2] = gray; } ctx.putImageData(imageData, 0, 0); saveHistory(); } function applyInvert() { const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; for (let i = 0; i < data.length; i += 4) { data[i] = 255 - data[i]; data[i + 1] = 255 - data[i + 1]; data[i + 2] = 255 - data[i + 2]; } ctx.putImageData(imageData, 0, 0); saveHistory(); } // Brush size control (mouse wheel) canvas.addEventListener('wheel', function(e) { if (e.ctrlKey) { e.preventDefault(); if (e.deltaY < 0) { brushSize = Math.min(brushSize + 1, 50); } else { brushSize = Math.max(brushSize - 1, 1); } console.log('Brush size:', brushSize); } }); // Touch support for mobile devices let touchDrawing = false; canvas.addEventListener('touchstart', function(e) { if (e.touches.length === 1) { const touch = e.touches[0]; const mouseEvent = new MouseEvent('mousedown', { clientX: touch.clientX, clientY: touch.clientY }); canvas.dispatchEvent(mouseEvent); touchDrawing = true; } }); canvas.addEventListener('touchmove', function(e) { if (touchDrawing && e.touches.length === 1) { e.preventDefault(); const touch = e.touches[0]; const mouseEvent = new MouseEvent('mousemove', { clientX: touch.clientX, clientY: touch.clientY }); canvas.dispatchEvent(mouseEvent); } }); canvas.addEventListener('touchend', function(e) { const mouseEvent = new MouseEvent('mouseup', {}); canvas.dispatchEvent(mouseEvent); touchDrawing = false; });