pixelforge-studio / script.js
Yukkkop's picture
Refine
a413231 verified
// 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 = `
<i data-feather="eye" class="w-4 h-4"></i>
<i data-feather="unlock" class="w-4 h-4"></i>
<div class="w-8 h-8 bg-gray-600 rounded"></div>
<span class="flex-1 text-sm">Layer ${layerCount}</span>
<span class="text-xs text-gray-400">100%</span>
`;
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;
});