diff --git a/cool.js b/archive/cool.js
similarity index 100%
rename from cool.js
rename to archive/cool.js
diff --git a/history.js b/archive/history.js
similarity index 100%
rename from history.js
rename to archive/history.js
diff --git a/archive/temp.css b/archive/temp.css
deleted file mode 100644
index 2383992..0000000
--- a/archive/temp.css
+++ /dev/null
@@ -1,25 +0,0 @@
-.puck {
- width: 100px;
- height: 120px;
- padding: 2px;
- background-color: #ddd;
- border: 1px solid;
- position: absolute;
- display: flex;
- flex-direction: column;
-}
-
-.well {
- width: 100%;
- flex-grow: 1;
- border: 1px solid;
-}
-
-.puck-menu {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- padding-top: 2px;
-}
-
-
diff --git a/archive/temp.js b/archive/temp.js
index 5076e70..fdb075f 100644
--- a/archive/temp.js
+++ b/archive/temp.js
@@ -1,124 +1,724 @@
-function createPuck(c) {
- const puck = document.createElement('div');
- puck.className = 'puck';
+const colorPreview = document.createElement('div');
+colorPreview.id = 'color-preview';
+colorPreview.className = 'puck';
+colorPreview.style.backgroundColor = color;
+
+menuBar.appendChild(colorPreview);
- const well = document.createElement('div');
- well.className = 'well';
- well.style.backgroundColor = c;
- puck.appendChild(well);
- const puckMenu = document.createElement('div');
- puckMenu.className = 'puck-menu';
- puck.appendChild(puckMenu);
+// }}}
- const selectHandle = document.createElement('div');
- selectHandle.className = 'select-handle';
- selectHandle.innerHTML = '';
- puckMenu.appendChild(selectHandle);
+// helpers {{{
- const copyHandle = document.createElement('div');
- copyHandle.className = 'copy-handle';
- copyHandle.innerHTML = '';
- puckMenu.appendChild(copyHandle);
- const deleteHandle = document.createElement('div');
- deleteHandle.className = 'delete-handle';
- deleteHandle.innerHTML = '';
- puckMenu.appendChild(deleteHandle);
+function saveCanvas() {
+ const dataURL = canvas.toDataURL();
+ const dimensions = `${canvas.width}x${canvas.height}`;
+ localStorage.setItem('mixxCanvas', dataURL);
+ localStorage.setItem('mixxDimensions', dimensions);
+ console.log('Canvas saved');
+}
+
+function loadCanvas() {
+ const dataURL = localStorage.getItem('mixxCanvas');
+ const dimensions = localStorage.getItem('mixxDimensions');
+ if (dataURL && dimensions) {
+ const img = new Image();
+ img.src = dataURL;
+ img.onload = function() {
+ canvas.width = dimensions.split('x')[0];
+ canvas.height = dimensions.split('x')[1];
+ canvas.style.width = canvas.width * zoom + 'px';
+ canvas.style.height = canvas.height * zoom + 'px';
+ canvasWidth = canvas.width;
+ canvasHeight = canvas.height;
+ ctx.drawImage(img, 0, 0);
+ }
+ } else {
+ console.log('No saved canvas found');
+ }
+}
- const updateHandle = document.createElement('div');
- updateHandle.className = 'update-handle';
- updateHandle.innerHTML = '';
- puckMenu.appendChild(updateHandle);
+function clearCanvasFromLocalStorage() {
+ localStorage.removeItem('savedCanvas');
+ localStorage.removeItem('canvasDimensions');
+}
+function saveState() {
+ if (undoStack.length >= maxHistory) {
+ undoStack.shift(); // Remove the oldest state if the stack exceeds the limit
+ }
- const dragHandle = document.createElement('div');
- dragHandle.className = 'drag-handle';
- dragHandle.innerHTML = '';
- puckMenu.appendChild(dragHandle);
+ undoStack.push({
+ imageData: canvas.toDataURL(),
+ width: canvas.width,
+ height: canvas.height
+ });
- well.addEventListener('mousedown', (e) => {
- let isMixing = true;
- let startX = e.clientX;
- let startY = e.clientY;
+ redoStack = []; // Clear the redo stack whenever a new action is performed
+}
- document.addEventListener('mousemove', onMouseMove);
- document.addEventListener('mouseup', onMouseUp);
+function undo() {
+ if (undoStack.length > 0) {
+ const currentState = {
+ imageData: canvas.toDataURL(),
+ width: canvas.width,
+ height: canvas.height
+ };
+
+ redoStack.push(currentState); // Save current state to the redo stack
+ const lastState = undoStack.pop(); // Get the last state from the undo stack
+
+ canvas.width = lastState.width;
+ canvas.height = lastState.height;
+ canvas.style.width = canvas.width * zoom + 'px';
+ canvas.style.height = canvas.height * zoom + 'px';
+ canvasWidth = canvas.width;
+ canvasHeight = canvas.height;
+
+ const img = new Image();
+ img.src = lastState.imageData;
+ img.onload = function() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.drawImage(img, 0, 0);
+ };
+ }
+}
+
+function redo() {
+ if (redoStack.length > 0) {
+ const currentState = {
+ imageData: canvas.toDataURL(),
+ width: canvas.width,
+ height: canvas.height
+ };
+
+ undoStack.push(currentState); // Save current state to the undo stack
+ const nextState = redoStack.pop(); // Get the last state from the redo stack
+
+ canvas.width = nextState.width;
+ canvas.height = nextState.height;
+ canvas.style.width = canvas.width * zoom + 'px';
+ canvas.style.height = canvas.height * zoom + 'px';
+ canvasWidth = canvas.width;
+ canvasHeight = canvas.height;
+
+ const img = new Image();
+ img.src = nextState.imageData;
+ img.onload = function() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.drawImage(img, 0, 0);
+ };
+ }
+}
- function onMouseMove(e) {
- if (isMixing) {
- const distance = Math.sqrt(Math.pow(e.clientX - startX, 2) + Math.pow(e.clientY - startY, 2));
+function getPositionOnCanvas(e) {
+ const rect = canvas.getBoundingClientRect();
+ return {
+ x: Math.round((e.clientX - rect.left) / zoom),
+ y: Math.round((e.clientY - rect.top) / zoom),
+ };
+}
+
+function drawCircle(x, y) {
+ ctx.beginPath();
+ ctx.arc(x, y, brushSize / 2, 0, 2 * Math.PI, false);
+ ctx.fillStyle = color;
+ ctx.fill();
+}
+
+function drawLineWithCircles(x1, y1, x2, y2) {
+ const dx = x2 - x1;
+ const dy = y2 - y1;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+ const steps = Math.ceil(distance / (brushSize / 5));
+
+ for (let i = 0; i <= steps; i++) {
+ const x = x1 + (dx * i) / steps;
+ const y = y1 + (dy * i) / steps;
+ drawCircle(x, y);
+ }
+}
- const t = Math.min(1, distance / 300);
+function saveCanvasContents() {
+ tempCanvas = document.createElement('canvas');
+ tempCanvas.width = canvas.width;
+ tempCanvas.height = canvas.height;
+ const tempCtx = tempCanvas.getContext('2d');
+ tempCtx.drawImage(canvas, 0, 0);
+}
- const mixedColor = mixbox.lerp(color, well.style.backgroundColor, t);
+function updateColorPreview() {
+ colorPreview.style.backgroundColor = color;
+}
- color = mixedColor;
+function hexToRgbArray(hex) {
+ if (hex.startsWith('#')) {
+ hex = hex.slice(1);
+ }
- startX = e.clientX;
- startY = e.clientY;
+ if (hex.length === 3) {
+ hex = hex.split('').map(char => char + char).join('');
+ }
+
+ const bigint = parseInt(hex, 16);
+ return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255];
+}
+
+function floodFill(x, y, fillColor) {
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ const data = imageData.data;
+
+ const targetColor = getColorAtPixel(data, x, y);
+ const fillColorArray = hexToRgbArray(fillColor);
+
+ if (colorsMatch(targetColor, fillColorArray)) {
+ return; // The clicked point is already the fill color
+ }
+
+ const stack = [{x, y}];
+
+ while (stack.length > 0) {
+ const {x, y} = stack.pop();
+ const currentColor = getColorAtPixel(data, x, y);
+
+ if (colorsMatch(currentColor, targetColor)) {
+ setColorAtPixel(data, x, y, fillColorArray);
+
+ if (x > 0) stack.push({x: x - 1, y});
+ if (x < canvas.width - 1) stack.push({x: x + 1, y});
+ if (y > 0) stack.push({x, y: y - 1});
+ if (y < canvas.height - 1) stack.push({x, y: y + 1});
+ }
+ }
+
+ ctx.putImageData(imageData, 0, 0);
+}
+
+function getColorAtPixel(data, x, y) {
+ const index = (y * canvas.width + x) * 4;
+ return [data[index], data[index + 1], data[index + 2], data[index + 3]];
+}
+
+function setColorAtPixel(data, x, y, color) {
+ const index = (y * canvas.width + x) * 4;
+ data[index] = color[0];
+ data[index + 1] = color[1];
+ data[index + 2] = color[2];
+ data[index + 3] = 255; // Set alpha to fully opaque
+}
+
+function colorsMatch(a, b) {
+ return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
+}
+
+// }}}
+
+// mousedown {{{
+
+canvasArea.addEventListener('mousedown', (e) => {
+ if (e.target.closest('.puck')) return;
+
+ startX = e.clientX;
+ startY = e.clientY;
+ canvasStartX = getPositionOnCanvas(e).x;
+ canvasStartY = getPositionOnCanvas(e).y;
+ saveCanvasContents();
+ isMouseDown = true;
+
+ if (
+ tool === 'brush' ||
+ tool === 'content-move' ||
+ tool === 'resize' ||
+ tool === 'zoom' ||
+ tool === 'bucket-fill'
+ ) {
+ saveState();
+ }
+
+ if (tool === 'brush') {
+ console.log('brush');
+ drawCircle(canvasStartX, canvasStartY);
+ } else if (tool === 'bucket-fill') {
+ floodFill(canvasStartX, canvasStartY, color);
+ return;
+ } else if (tool === 'move') {
+ startX = e.clientX - canvasContainer.offsetLeft;
+ startY = e.clientY - canvasContainer.offsetTop;
+ } else if (tool === 'color-picker') {
+ const imageData = ctx.getImageData(canvasStartX, canvasStartY, 1, 1).data;
+ const pickedColor = `rgb(${imageData[0]}, ${imageData[1]}, ${imageData[2]})`;
+ color = pickedColor;
+ console.log('Picked Color:', pickedColor);
+ updateColorPreview();
+ return;
+ }
+});
+
+// }}}
+
+// mousemove {{{
+
+canvasArea.addEventListener('mousemove', (e) => {
+
+ endX = e.clientX;
+ endY = e.clientY;
+ dX = endX - startX;
+ dY = endY - startY;
+
+ canvasEndX = getPositionOnCanvas(e).x;
+ canvasEndY = getPositionOnCanvas(e).y;
+ canvasDX = canvasEndX - canvasStartX;
+ canvasDY = canvasEndY - canvasStartY;
+
+ if (tool == 'brush-size') {
+ brushPreview.style.display = 'block';
+ brushPreview.style.width = brushSize + 'px';
+ brushPreview.style.height = brushSize + 'px';
+ brushPreview.style.left = e.clientX - brushSize / 2 + 'px';
+ brushPreview.style.top = e.clientY - brushSize / 2 + 'px';
+ }
+
+ if (isMouseDown) {
+
+ if (tool === 'brush-size') {
+ brushSize += dX * dBrushSize;
+ if (brushSize < 1) brushSize = 1;
+ if (brushSize > maxBrushSize) brushSize = maxBrushSize;
+ startX = endX;
+ } else if (tool === 'brush') {
+ drawLineWithCircles(canvasStartX, canvasStartY, canvasEndX, canvasEndY);
+
+ canvasStartX = canvasEndX;
+ canvasStartY = canvasEndY;
+
+ } else if (tool === 'content-move') {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.fillStyle = backgroundColor;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.drawImage(tempCanvas, dX, dY);
+ } else if (tool === 'move') {
+ canvasContainer.style.left = dX + 'px';
+ canvasContainer.style.top = dY + 'px';
+ } else if (tool === 'zoom') {
+ zoom += dX * dZoom;
+ if (zoom < 0.1) zoom = 0.1;
+ canvas.style.height = canvasHeight * zoom + 'px';
+ canvas.style.width = canvasWidth * zoom + 'px';
+ startX = endX;
+ } else if (tool === 'resize') {
+ let newWidth = canvasWidth + dX / zoom;
+ let newHeight = canvasHeight + dY / zoom;
+ if (newWidth > 0 && newHeight > 0) {
+ canvas.width = newWidth;
+ canvas.height = newHeight;
+ canvas.style.width = newWidth * zoom + 'px';
+ canvas.style.height = newHeight * zoom + 'px';
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.fillStyle = backgroundColor;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.drawImage(tempCanvas, 0, 0);
}
- }
+ } else if (tool === 'color-mix') {
- function onMouseUp() {
- isMixing = false;
- document.removeEventListener('mousemove', onMouseMove);
- document.removeEventListener('mouseup', onMouseUp);
+ const imageData = ctx.getImageData(canvasEndX, canvasEndY, 1, 1).data;
+ const canvasColor = `rgb(${imageData[0]}, ${imageData[1]}, ${imageData[2]})`;
+
+ const distance = Math.sqrt(Math.pow(e.clientX - startX, 2) + Math.pow(e.clientY - startY, 2));
+ const t = Math.min(1, distance / 300);
+
+ const mixedColor = mixbox.lerp(color, canvasColor, t);
+
+ color = mixedColor;
+
+ startX = e.clientX;
+ startY = e.clientY;
+
+ }
+
+ }
+
+ updateInfos();
+ updateColorPreview();
+});
+
+// }}}
+
+// mouseup {{{
+
+canvasArea.addEventListener('mouseup', (e) => {
+ isMouseDown = false;
+ if (tool === 'brush') {
+ ctx.closePath();
+ } else if (tool === 'resize') {
+ canvasWidth = canvas.width;
+ canvasHeight = canvas.height;
+ }
+
+ updateColorPreview();
+});
+
+// }}}
+
+// mouseleave {{{
+
+canvasArea.addEventListener('mouseleave', (e) => {
+ isMouseDown = false;
+ brushPreview.style.display = 'none';
+});
+
+// }}}
+
+// keybindings {{{
+
+const toolKeyBindings = {}
+
+const functionKeyBindings = {
+}
+
+document.addEventListener('keydown', (e) => {
+ if (keyDown) return;
+
+ const newTool = toolKeyBindings[e.key.toLowerCase()]
+ if (newTool) {
+ prevTool = tool;
+ keyDown = true;
+ changeTool(newTool);
+ return;
+ }
+
+ const func = functionKeyBindings[e.key];
+ if (func) {
+ func();
+ return;
+ }
+
+});
+
+document.addEventListener('keyup', (e) => {
+ const currentTool = toolKeyBindings[e.key.toLowerCase()]
+ if (currentTool) {
+ keyDown = false;
+ if (e.key == e.key.toLowerCase()) {
+ changeTool(prevTool);
+ return;
}
+ }
+});
+
+// }}}
+
+// tools {{{
+
+var toolButtons = [];
+
+function changeTool(toolName) {
+ toolButtons.forEach(button => button.button.classList.remove('active'));
+ toolButtons.find(button => button.name === toolName).button.classList.add('active');
+ tool = toolName;
+ brushPreview.style.display = 'none';
+ updateInfos();
+}
+
+function createToolButton(toolName, displayName, icon, key=undefined) {
+ const button = document.createElement('div');
+ button.classList.add('button');
+ button.classList.add('tool');
+ button.innerHTML = icon;
+ button.title = displayName;
+ button.addEventListener('click', () => {
+ changeTool(toolName);
});
- dragHandle.addEventListener('mousedown', (e) => {
- let isMovingPuck = true;
- let startX = e.clientX;
- let startY = e.clientY;
- let left = puck.offsetLeft;
- let top = puck.offsetTop;
- document.addEventListener('mousemove', (e) => {
- if (isMovingPuck) {
- puck.style.left = left + e.clientX - startX + 'px';
- puck.style.top = top + e.clientY - startY + 'px';
+ if (key) {
+ const keyHint = document.createElement('span');
+ keyHint.className = 'key-hint';
+ keyHint.innerHTML = key;
+ button.appendChild(keyHint);
+ toolKeyBindings[key] = toolName;
+ }
+
+ toolBar.appendChild(button);
+ toolButtons.push({'name': toolName, 'button': button});
+ return button;
+}
+
+createToolButton('brush', 'Brush', '', 'b');
+createToolButton('content-move', 'Move Content', '', 'h');
+createToolButton('move', 'Move Canvas', '', 'm');
+createToolButton('zoom', 'Zoom', '', 'z');
+createToolButton('resize', 'Resize', '', 'r');
+createToolButton('color-picker', 'Color Picker', '', 'a');
+createToolButton('color-mix', 'Color Mix', '', 'x');
+createToolButton('brush-size', 'Brush Size', '', 'd');
+createToolButton('bucket-fill', 'Bucket Fill', '', 'k');
+
+// }}}
+
+// menu functons {{{
+
+function flipCanvasHorizontally(e) {
+ saveState();
+ ctx.save();
+ saveCanvasContents();
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.scale(-1, 1);
+ ctx.translate(-canvas.width, 0);
+ ctx.drawImage(tempCanvas, 0, 0);
+ ctx.restore();
+}
+
+function flipCanvasVertically(e) {
+ saveState();
+ ctx.save();
+ saveCanvasContents();
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.scale(1, -1);
+ ctx.translate(0, -canvas.height);
+ ctx.drawImage(tempCanvas, 0, 0);
+ ctx.restore();
+}
+
+function exportCanvas(e) {
+ const link = document.createElement('a');
+ link.download = 'canvas.png';
+ link.href = canvas.toDataURL();
+ link.click();
+}
+
+function importCanvas(e) {
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = 'image/*';
+ input.onchange = (e) => {
+ const file = e.target.files[0];
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ const img = new Image();
+ img.onload = () => {
+ canvas.width = img.width;
+ canvas.height = img.height;
+ ctx.drawImage(img, 0, 0);
}
+ img.src = e.target.result;
+ }
+ reader.readAsDataURL(file);
+ }
+ input.click();
+}
+
+function clearCanvas(e) {
+ saveState();
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.fillStyle = backgroundColor;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+}
+
+function resetZoom(e) {
+ zoom = 1;
+ canvas.style.width = canvas.width * zoom + 'px';
+ canvas.style.height = canvas.height * zoom + 'px';
+ canvasWidth = canvas.width;
+ canvasHeight = canvas.height;
+
+ canvasAreaRect = canvasArea.getBoundingClientRect();
+
+ canvasContainer.style.left = `${canvasAreaRect.left}px`;
+ canvasContainer.style.top = `${canvasAreaRect.top}px`;
+}
+
+// }}}
+
+// menu {{{
+
+var menuButtons = [];
+
+function createMenuButton(icon, name, clickFunction, key=undefined) {
+ const button = document.createElement('div');
+ button.className = 'button';
+ button.innerHTML = icon;
+ button.title = name;
+ if (clickFunction) {
+ button.addEventListener('click', () => {
+ clickFunction()
+ updateInfos();
});
- document.addEventListener('mouseup', () => {
- isMovingPuck = false;
- document.removeEventListener('mousemove', () => {});
- document.removeEventListener('mouseup', () => {});
+ }
+ menuBar.appendChild(button);
+ if (key) {
+ const keyHint = document.createElement('span');
+ keyHint.className = 'key-hint';
+ keyHint.innerHTML = key;
+ button.appendChild(keyHint);
+ functionKeyBindings[key] = clickFunction;
+ }
+ return button;
+}
+
+menuButtons.push(createMenuButton('', 'Save', saveCanvas, 's'));
+menuButtons.push(createMenuButton('', 'Load', loadCanvas));
+menuButtons.push(createMenuButton('', 'Clear', clearCanvas));
+menuButtons.push(createMenuButton('', 'Export', exportCanvas));
+menuButtons.push(createMenuButton('', 'Import', importCanvas));
+
+
+menuButtons.push(createMenuButton('', 'Flip Horizontally', flipCanvasHorizontally, 'f'));
+menuButtons.push(createMenuButton('', 'Flip Vertically', flipCanvasVertically, 'v'));
+menuButtons.push(createMenuButton('', 'Undo', undo, 'u'));
+menuButtons.push(createMenuButton('', 'Redo', redo, 'y'));
+menuButtons.push(createMenuButton('', 'Clear', clearCanvas, 'c'));
+menuButtons.push(createMenuButton('', 'Reset', resetZoom, 't'));
+menuButtons.push(createMenuButton('', 'Add Color', createPuck));
+
+// }}}
+
+// pucks {{{
+
+function createPuck(c, editable=true, key=undefined) {
+ if (c === undefined) {
+ c = color;
+ }
+
+ const puck = document.createElement('div');
+ puck.className = 'puck';
+ puck.style.backgroundColor = c;
+
+ // const selectHandle = document.createElement('div');
+ // selectHandle.className = 'select-handle';
+ // selectHandle.innerHTML = '';
+ // puck.appendChild(selectHandle);
+
+ // selectHandle.addEventListener('click', () => {
+ // color = puck.style.backgroundColor;
+ // updateColorPreview();
+ // updateInfos();
+ // });
+
+ if (editable) {
+ // const updateHandle = document.createElement('div');
+ // updateHandle.className = 'update-handle';
+ // updateHandle.innerHTML = '';
+ // puck.appendChild(updateHandle);
+
+ // updateHandle.addEventListener('click', () => {
+ // puck.style.backgroundColor = color;
+ // });
+
+ const deleteHandle = document.createElement('div');
+ deleteHandle.className = 'delete-handle';
+ deleteHandle.innerHTML = '';
+ puck.appendChild(deleteHandle);
+
+ deleteHandle.addEventListener('click', () => {
+ console.log("test");
+ puck.remove();
});
- });
+ }
- updateHandle.addEventListener('click', () => {
- console.log('update');
- well.style.backgroundColor = color;
- });
+ if (key) {
+ const keyHint = document.createElement('div');
+ keyHint.className = 'key-hint';
+ keyHint.innerHTML = key;
+ puck.appendChild(keyHint);
+ }
- selectHandle.addEventListener('click', () => {
- console.log('select');
- color = well.style.backgroundColor;
- console.log(color);
- updateColorPreview();
- });
+ function mixx(startTime) {
+ var interval = setInterval(() => {
+ const elapsedTime = Date.now() - startTime;
+ const t = Math.min(1, elapsedTime / 10000);
+ const mixedColor = mixbox.lerp(color, puck.style.backgroundColor, t);
+ color = mixedColor;
+ updateColorPreview();
+ updateInfos();
+ }, 50);
+ return interval;
+ }
- copyHandle.addEventListener('click', () => {
- pucks.push(createPuck(well.style.backgroundColor));
+ puck.addEventListener('mousedown', (e) => {
+ const startTime = Date.now();
+ var interval = mixx(startTime);
+ function onMouseUp() {
+ clearInterval(interval);
+ document.removeEventListener('mouseup', onMouseUp);
+ }
+ document.addEventListener('mouseup', onMouseUp);
});
- deleteHandle.addEventListener('click', () => {
- pucks = pucks.filter(p => p !== puck);
- puck.remove();
+ document.addEventListener('keydown', (e) => {
+ if (e.key == key) {
+ console.log(e.key);
+ const startTime = Date.now();
+ var interval = mixx(startTime);
+ function onKeyUp() {
+ clearInterval(interval);
+ document.removeEventListener('keyup', onKeyUp);
+ }
+ document.addEventListener('keyup', onKeyUp);
+ }
});
+ menuBar.appendChild(puck);
+}
+
+createPuck(c='rgb(0, 0, 0)', editable=false, key='1');
+createPuck(c='rgb(255, 0, 0)', editale=false, key='2');
+createPuck(c='rgb(0, 0, 255)', editale=false, key='3');
+createPuck(c='rgb(255, 255, 0)', editale=false, key='4');
+createPuck(c='rgb(99, 60, 22)', editale=false, key='5');
+createPuck(c='rgb(0, 255, 0)', editale=false, key='6');
+createPuck(c='rgb(255, 0, 255)', editale=false, key='7');
+createPuck(c='rgb(0, 255, 255)', editale=false, key='8');
+createPuck(c='rgb(255, 255, 255)', editale=false, key='9');
- canvasArea.appendChild(puck);
- let canvasWidth = canvasArea.offsetWidth;
- let canvasHeight = canvasArea.offsetHeight;
+// }}}
- let randonX = Math.floor(Math.random() * canvasWidth);
- let randonY = Math.floor(Math.random() * canvasHeight);
+// info {{{
- puck.style.left = randonX + 'px';
- puck.style.top = randonY + 'px';
+var infos = [];
- return puck;
+function createInfo(name, updateFunction) {
+ const info = document.createElement('span');
+ info.className = 'info';
+ const key = document.createElement('span');
+ key.className = 'key';
+ key.innerHTML = name + ':';
+ const value = document.createElement('span');
+ value.className = 'value';
+ value.innerHTML = '0';
+ info.appendChild(key);
+ info.appendChild(value);
+ infoBar.appendChild(info);
+ function update() {
+ let v = updateFunction();
+ if (v === undefined) v = '?';
+ value.innerHTML = v;
+ }
+ return update;
}
+infos.push(createInfo('zoom', function() {
+ var percent = zoom * 100;
+ return percent.toFixed(0) + '%';
+}));
+infos.push(createInfo('brush', function() { return brushSize; }));
+infos.push(createInfo('x', function() { return canvasEndX; }));
+infos.push(createInfo('y', function() { return canvasEndY; }));
+infos.push(createInfo('color', function() { return color; }));
+infos.push(createInfo('width', function() { return canvas.width; }));
+infos.push(createInfo('height', function() { return canvas.height; }));
+
+function updateInfos() {
+ infos.forEach(info => info());
+}
+
+// }}}
+
+// start {{{
+
+ctx.fillStyle = backgroundColor;
+ctx.fillRect(0, 0, canvas.width, canvas.height);
+updateInfos();
+toolButtons[0]['button'].click();
+
+// }}}
diff --git a/temp2.js b/archive/temp2.js
similarity index 100%
rename from temp2.js
rename to archive/temp2.js
diff --git a/render.js b/render.js
index 2534771..dda3d59 100644
--- a/render.js
+++ b/render.js
@@ -6,7 +6,6 @@ const layerControllersElement = document.getElementById('layer-controllers');
const studioElement = document.getElementById('studio');
const infoBarElement = document.getElementById('info-bar');
const easelElement = document.getElementById('easel');
-const brushPreviewElement = document.getElementById('brush-preview');
const dZoom = 0.001;
const dBrushSize = 0.5;
@@ -41,6 +40,7 @@ let isKeyDown = false;
let isMouseDown = false;
let interval;
+var startTime;
// }}}
@@ -56,15 +56,14 @@ function disableImageSmoothing(ctx) {
};
function hexToRgbArray(hex) {
- hex = hex.replace(/^#/, '');
- const bigint = parseInt(hex, 16);
- const r = (bigint >> 16) & 255;
- const g = (bigint >> 8) & 255;
- const b = bigint & 255;
- return [r, g, b, 255]; // Add 255 for full opacity
+ const r = parseInt(hex.substring(1, 3), 16);
+ const g = parseInt(hex.substring(3, 5), 16);
+ const b = parseInt(hex.substring(5, 7), 16);
+ return [r, g, b, 255];
}
function colorsMatch(color1, color2, tolerance = 0) {
+ return color1[0] === color2[0] && color1[1] === color2[1] && color1[2] === color2[2] && color1[3] === color2[3];
return Math.abs(color1[0] - color2[0]) <= tolerance &&
Math.abs(color1[1] - color2[1]) <= tolerance &&
Math.abs(color1[2] - color2[2]) <= tolerance &&
@@ -78,6 +77,10 @@ function makeIconElement(htmlString) {
return iconElement;
}
+function closeRgbArray(color1, color2, tolerance) {
+ return Math.abs(color1[0] - color2[0]) <= tolerance && Math.abs(color1[1] - color2[1]) <= tolerance && Math.abs(color1[2] - color2[2]) <= tolerance;
+}
+
function makeButtonElement({icon, name, func, key}) {
if (!icon) throw new Error('No icon provided');
if (!name) throw new Error('No name provided');
@@ -118,10 +121,33 @@ function makeButtonElement({icon, name, func, key}) {
function makeColor(rgb) {
const color = {};
- color.color = rgb;
+ color.fromRgba = function(rgba) {
+ color.r = rgba.split(',')[0].split('(')[1];
+ color.g = rgba.split(',')[1];
+ color.b = rgba.split(',')[2];
+ color.a = rgba.split(',')[3].split(')')[0];
+ }
+
+ color.toRgba = function() {
+ return `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`;
+ }
+
+ color.fromRgb = function(rgb) {
+ color.r = rgb.split(',')[0].split('(')[1];
+ color.g = rgb.split(',')[1];
+ color.b = rgb.split(',')[2].split(')')[0];
+ color.a = 255;
+ }
color.toRgb = function() {
- return color.rgb;
+ return `rgb(${color.r}, ${color.g}, ${color.b})`;
+ }
+
+ color.fromHex = function(hex) {
+ color.r = parseInt(hex.substring(1, 3), 16);
+ color.g = parseInt(hex.substring(3, 5), 16);
+ color.b = parseInt(hex.substring(5, 7), 16);
+ color.a = 255;
}
color.toHex = function() {
@@ -131,17 +157,95 @@ function makeColor(rgb) {
return `#${color.r.toString(16)}${color.g.toString(16)}${color.b.toString(16)}`;
}
- color.mix = function(color2, t) {
- const color1 = color.color;
- const newColor = mixbox.lerp(color1, color2, t);
- color.color = newColor;
- colorPreview.update();
+ color.fromRgbaArray = function(array) {
+ color.r = array[0];
+ color.g = array[1];
+ color.b = array[2];
+ color.a = array[3];
+ }
+
+ color.toRgbaArray = function() {
+ return [color.r, color.g, color.b, color.a];
+ }
+
+ color.fromRgbArray = function(array) {
+ color.r = array[0];
+ color.g = array[1];
+ color.b = array[2];
+ color.a = 255;
}
+ color.toRgbArray = function() {
+ return [color.r, color.g, color.b];
+ }
+
+ color.mixxRgbArray = function(color2RgbArray, t) {
+ const color1RgbArray = color.toRgbArray();
+ if (color1RgbArray === color2RgbArray) {
+ return;
+ }
+ var newColorRgbArray = color2RgbArray;
+ if (!closeRgbArray(color1RgbArray, color2RgbArray, 2)) {
+ console.log('mixxing');
+ newColorRgbArray = mixbox.lerp(color1RgbArray, color2RgbArray, t);
+ }
+ color.fromRgbArray(newColorRgbArray);
+ }
+
+ color.mixxRgb = function(color2Rgb, t) {
+ const result = color2Rgb.match(/rgb\((\d+), (\d+), (\d+)\)/);
+ if (result) {
+ const color2RgbArray = [result[1], result[2], result[3]];
+ color.mixxRgbArray(color2RgbArray, t);
+ }
+ }
+
+ color.mixxRgbaArray = function(color2RgbaArray, t) {
+ const color2a = color2RgbaArray[3];
+ if (color2a !== 225) {
+ return;
+ }
+ const color2RgbArray = color2RgbaArray.slice(0, 3);
+ color.mixxRgbArray(color2RgbArray, t);
+ }
+
+ color.mixxRgba = function(color2Rgba, t) {
+ const result = color2Rgba.match(/rgba\((\d+), (\d+), (\d+), (\d+)\)/);
+ if (result) {
+ const color2RgbaArray = [result[1], result[2], result[3], result[4]];
+ color.mixxRgbaArray(color2RgbaArray, t);
+ }
+ }
+
+ color.mixx = function(color2, t) {
+ if (color2.a === 255) {
+ color.mixxRgbArray(color2.toRgbArray(), t);
+ }
+ }
+
+ color.isOpaque = function() {
+ return color.a === 255;
+ }
+
+ color.match = function(color2) {
+ return color.r === color2.r && color.g === color2.g && color.b === color2.b && color.a === color2.a;
+ }
+
+ color.copy = function(color2) {
+ color.r = color2.r;
+ color.g = color2.g;
+ color.b = color2.b;
+ color.a = color2.a;
+ }
+
+ color.fromRgb(rgb);
+
return color;
}
-const color = makeColor('rgb(0, 0, 0)');
+const brushColor = makeColor('rgb(0, 0, 0)');
+const canvasColor = makeColor('rgb(0, 0, 0)');
+const tempColor = makeColor('rgb(0, 0, 0)');
// }}}
@@ -149,10 +253,11 @@ const color = makeColor('rgb(0, 0, 0)');
// FACTORY {{{
-function makeCanvas({height=600, width=800}) { // {{{
+function makeCanvas({height=600, width=800, background=false}) { // {{{
const canvas = document.createElement('canvas');
canvas.style.imageRendering = 'pixelated';
canvas.ctx = canvas.getContext('2d');
+ canvas.background = background;
canvas.tempCanvas = document.createElement('canvas');
canvas.tempCtx = canvas.tempCanvas.getContext('2d');
@@ -167,7 +272,9 @@ function makeCanvas({height=600, width=800}) { // {{{
}
canvas.clearCanvas = function() {
- canvas.ctx.clearRect(0, 0, canvas.width, canvas.height);
+ if (!canvas.background) {
+ canvas.ctx.clearRect(0, 0, canvas.width, canvas.height);
+ }
}
canvas.restoreCanvas = function(x=0, y=0) {
@@ -185,9 +292,14 @@ function makeCanvas({height=600, width=800}) { // {{{
};
canvas.resize = function(width, height) {
+ canvas.saveCanvas();
+ canvas.clearCanvas();
canvas.width = width;
canvas.height = height;
+ canvas.style.width = width * zoom + 'px';
+ canvas.style.height = height * zoom + 'px';
disableImageSmoothing(canvas.ctx);
+ canvas.restoreCanvas();
}
canvas.getPositionOnCanvas = function(e) {
@@ -199,87 +311,95 @@ function makeCanvas({height=600, width=800}) { // {{{
}
canvas.drawPixel = function(x, y, color) {
- canvas.ctx.fillStyle = color;
- canvas.ctx.fillRect(x, y, 1, 1);
+ if (!canvas.background) {
+ canvas.ctx.fillStyle = color;
+ canvas.ctx.fillRect(x, y, 1, 1);
+ }
}
canvas.drawLineWithPixels = function(x1, y1, x2, y2, color) {
- const dx = Math.abs(x2 - x1);
- const dy = Math.abs(y2 - y1);
- const sx = x1 < x2 ? 1 : -1;
- const sy = y1 < y2 ? 1 : -1;
- let err = dx - dy;
- while (true) {
- canvas.drawPixel(x1, y1, color); // Draw each pixel along the line
- if (x1 === x2 && y1 === y2) break;
- const e2 = err * 2;
- if (e2 > -dy) { err -= dy; x1 += sx; }
- if (e2 < dx) { err += dx; y1 += sy; }
+ if (!canvas.background) {
+ const dx = Math.abs(x2 - x1);
+ const dy = Math.abs(y2 - y1);
+ const sx = x1 < x2 ? 1 : -1;
+ const sy = y1 < y2 ? 1 : -1;
+ let err = dx - dy;
+ while (true) {
+ canvas.drawPixel(x1, y1, color); // Draw each pixel along the line
+ if (x1 === x2 && y1 === y2) break;
+ const e2 = err * 2;
+ if (e2 > -dy) { err -= dy; x1 += sx; }
+ if (e2 < dx) { err += dx; y1 += sy; }
+ }
}
}
canvas.drawShape = function(x, y, shape, size, color) {
- x = Math.round(x);
- y = Math.round(y);
+ if (!canvas.background) {
+ x = Math.round(x);
+ y = Math.round(y);
- if (size === 1) {
- canvas.drawPixel(x, y, color);
- return;
- }
- canvas.ctx.fillStyle = color;
-
- if (shape === 'square') {
- canvas.ctx.fillRect(x - Math.floor(size / 2), y - Math.floor(size / 2), size, size);
- } else if (shape === 'circle') {
- let radius = Math.floor(size / 1);
- let radiusSquared = radius * radius;
-
- for (let y1 = -radius; y1 <= radius; y1++) {
- for (let x1 = -radius; x1 <= radius; x1++) {
- // Adjust the condition to avoid the outcrop
- if ((x1 * x1 + y1 * y1) <= radiusSquared - radius) {
- canvas.ctx.fillRect(x + x1, y + y1, 1, 1);
+ if (size === 1) {
+ canvas.drawPixel(x, y, color);
+ return;
+ }
+ canvas.ctx.fillStyle = color;
+
+ if (shape === 'square') {
+ canvas.ctx.fillRect(x - Math.floor(size / 2), y - Math.floor(size / 2), size, size);
+ } else if (shape === 'circle') {
+ let radius = Math.floor(size / 2);
+ let radiusSquared = radius * radius;
+
+ for (let y1 = -radius; y1 <= radius; y1++) {
+ for (let x1 = -radius; x1 <= radius; x1++) {
+ // Adjust the condition to avoid the outcrop
+ if ((x1 * x1 + y1 * y1) <= radiusSquared - radius) {
+ canvas.ctx.fillRect(x + x1, y + y1, 1, 1);
+ }
}
}
- }
- } else if (shape === 'empty-circle') {
- let radius = Math.floor(size / 2);
- let x1 = radius;
- let y1 = 0;
- let radiusError = 1 - x1;
-
- while (x1 >= y1) {
- // Draw the 8 octants of the circle
- canvas.ctx.fillRect(x + x1, y + y1, 1, 1);
- canvas.ctx.fillRect(x + y1, y + x1, 1, 1);
- canvas.ctx.fillRect(x - y1, y + x1, 1, 1);
- canvas.ctx.fillRect(x - x1, y + y1, 1, 1);
- canvas.ctx.fillRect(x - x1, y - y1, 1, 1);
- canvas.ctx.fillRect(x - y1, y - x1, 1, 1);
- canvas.ctx.fillRect(x + y1, y - x1, 1, 1);
- canvas.ctx.fillRect(x + x1, y - y1, 1, 1);
-
- y1++;
- if (radiusError < 0) {
- radiusError += 2 * y1 + 1;
- } else {
- x1--;
- radiusError += 2 * (y1 - x1 + 1);
+ } else if (shape === 'empty-circle') {
+ let radius = Math.floor(size / 2);
+ let x1 = radius;
+ let y1 = 0;
+ let radiusError = 1 - x1;
+
+ while (x1 >= y1) {
+ // Draw the 8 octants of the circle
+ canvas.ctx.fillRect(x + x1, y + y1, 1, 1);
+ canvas.ctx.fillRect(x + y1, y + x1, 1, 1);
+ canvas.ctx.fillRect(x - y1, y + x1, 1, 1);
+ canvas.ctx.fillRect(x - x1, y + y1, 1, 1);
+ canvas.ctx.fillRect(x - x1, y - y1, 1, 1);
+ canvas.ctx.fillRect(x - y1, y - x1, 1, 1);
+ canvas.ctx.fillRect(x + y1, y - x1, 1, 1);
+ canvas.ctx.fillRect(x + x1, y - y1, 1, 1);
+
+ y1++;
+ if (radiusError < 0) {
+ radiusError += 2 * y1 + 1;
+ } else {
+ x1--;
+ radiusError += 2 * (y1 - x1 + 1);
+ }
}
}
}
}
canvas.drawLineWithShape = function(x1, y1, x2, y2, shape, size, color) {
- const dx = x2 - x1;
- const dy = y2 - y1;
- const distance = Math.sqrt(dx * dx + dy * dy);
- const steps = Math.ceil(distance / (size / 2));
-
- for (let i = 0; i <= steps; i++) {
- const x = Math.round(x1 + (dx * i) / steps);
- const y = Math.round(y1 + (dy * i) / steps);
- canvas.drawShape(x, y, shape, size, color);
+ if (!canvas.background) {
+ const dx = x2 - x1;
+ const dy = y2 - y1;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+ const steps = Math.ceil(distance / (size / 2));
+
+ for (let i = 0; i <= steps; i++) {
+ const x = Math.round(x1 + (dx * i) / steps);
+ const y = Math.round(y1 + (dy * i) / steps);
+ canvas.drawShape(x, y, shape, size, color);
+ }
}
}
@@ -288,13 +408,13 @@ function makeCanvas({height=600, width=800}) { // {{{
canvas.ctx.fillRect(0, 0, canvas.width, canvas.height);
}
- canvas.getColorAtPixelData = function(x, y, data) {
+ canvas.getRgbaArrayColorAtPixelData = function(x, y, data) {
const index = (y * canvas.width + x) * 4;
const color = [data[index], data[index + 1], data[index + 2], data[index + 3]];
return color;
}
- canvas.setColorAtPixelData = function(x, y, color, data) {
+ canvas.setRgbaArrayColorAtPixelData = function(x, y, color, data) {
const index = (y * canvas.width + x) * 4;
data[index] = color[0];
data[index + 1] = color[1];
@@ -302,46 +422,53 @@ function makeCanvas({height=600, width=800}) { // {{{
data[index + 3] = color[3];
}
- canvas.floodFill = function(x, y, color) {
- const imageData = canvas.ctx.getImageData(0, 0, canvas.width, canvas.height);
- const data = imageData.data;
+ canvas.floodFill = function(x, y, colorRgbaArray) {
+ if (!canvas.background) {
+ const imageData = canvas.ctx.getImageData(0, 0, canvas.width, canvas.height);
+ const data = imageData.data;
- const targetColor = canvas.getColorAtPixelData(x, y, data);
- const fillColorArray = hexToRgbArray(color);
+ const targetColor = canvas.getRgbaArrayColorAtPixelData(x, y, data);
+ const fillColorArray = colorRgbaArray;
- if (colorsMatch(targetColor, fillColorArray, tolerance)) {
- return;
- }
+ if (colorsMatch(targetColor, fillColorArray, tolerance)) {
+ return;
+ }
- const stack = [{x, y}];
+ const stack = [{x, y}];
- while (stack.length > 0) {
- const {x, y} = stack.pop();
- const currentColor = canvas.getColorAtPixelData(x, y, data);
+ while (stack.length > 0) {
+ const {x, y} = stack.pop();
+ const currentColor = canvas.getRgbaArrayColorAtPixelData(x, y, data);
- if (colorsMatch(currentColor, targetColor, tolerance)) {
- canvas.setColorAtPixelData(x, y, fillColorArray, data);
+ if (colorsMatch(currentColor, targetColor, tolerance)) {
+ canvas.setRgbaArrayColorAtPixelData(x, y, fillColorArray, data);
- if (x > 0) stack.push({x: x - 1, y});
- if (x < canvas.width - 1) stack.push({x: x + 1, y});
- if (y > 0) stack.push({x, y: y - 1});
- if (y < canvas.height - 1) stack.push({x, y: y + 1});
+ if (x > 0) stack.push({x: x - 1, y});
+ if (x < canvas.width - 1) stack.push({x: x + 1, y});
+ if (y > 0) stack.push({x, y: y - 1});
+ if (y < canvas.height - 1) stack.push({x, y: y + 1});
+ }
}
- }
- canvas.ctx.putImageData(imageData, 0, 0);
+ canvas.ctx.putImageData(imageData, 0, 0);
+ }
}
- canvas.getColorAtPixel = function(x, y) {
+ canvas.getRgbaColorArrayAtPixel = function(x, y) {
const data = canvas.ctx.getImageData(0, 0, canvas.width, canvas.height).data;
- return canvas.getColorAtPixelData(x, y, data);
+ return canvas.getRgbaArrayColorAtPixelData(x, y, data);
+ }
+
+ canvas.getRgbColorAtPixel = function(x, y) {
+ const color = canvas.getRgbaColorArrayAtPixel(x, y);
+ return `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
}
canvas.setColorAtPixel = function(x, y, color) {
const imageData = canvas.ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
- canvas.setColorAtPixelData(x, y, color, data);
+ canvas.setRgbaArrayColorAtPixelData(x, y, color, data);
canvas.ctx.putImageData(new ImageData(data, canvas.width, canvas.height), 0, 0);
}
@@ -364,7 +491,9 @@ function makeCanvas({height=600, width=800}) { // {{{
}
canvas.deleteCanvas = function() {
- canvas.remove();
+ if (!background) {
+ canvas.remove();
+ }
}
canvas.setWidth(width);
@@ -374,40 +503,17 @@ function makeCanvas({height=600, width=800}) { // {{{
} // }}}
-function makeLayer({height=600, width=800}) { // {{{
+function makeLayer({height=600, width=800, background=undefined}) { // {{{
const layer = {}
- layer.canvas = makeCanvas({height, width});
+ layer.canvas = makeCanvas({height, width, background});
layer.active = false;
layer.opacity = 1;
+ layer.background = background;
layer.controllerElement = document.createElement('div');
layer.controllerElement.className = 'layer-controller';
layer.controllerElement.innerHTML = '';
- const moveUpHandle = document.createElement('div');
- moveUpHandle.classList.add('handle');
- moveUpHandle.classList.add('top-right');
- moveUpHandle.innerHTML = '';
- moveUpHandle.addEventListener('click', () => {
- const index = layers.indexOf(layer);
- if (index > 0) {
- layers.move(layer, index - 1);
- }
- });
- layer.controllerElement.appendChild(moveUpHandle);
-
- const moveDownHandle = document.createElement('div');
- moveDownHandle.classList.add('handle');
- moveDownHandle.classList.add('bottom-right');
- moveDownHandle.innerHTML = '';
- moveDownHandle.addEventListener('click', () => {
- const index = layers.indexOf(layer);
- if (index < layers.length - 1) {
- layers.move(layer, index + 1);
- }
- });
- layer.controllerElement.appendChild(moveDownHandle);
-
layer.controllerElement.addEventListener('click', () => {
layers.setActive(layer);
});
@@ -425,7 +531,7 @@ function makeLayer({height=600, width=800}) { // {{{
return layer;
} // }}}
-function makeLayers({height=600, width=800}) { // {{{
+function makeLayers({height=600, width=800, backgroundColor='rgb(255, 255, 255)'}) { // {{{
const layers = [];
layers.height = height;
layers.width = width;
@@ -439,6 +545,7 @@ function makeLayers({height=600, width=800}) { // {{{
layers.setHeight = function(height) {
layers.height = height;
+ layers.forEach(layer => layer.canvas.setHeight(height));
easelElement.style.height = height + 2 + 'px';
}
@@ -446,11 +553,30 @@ function makeLayers({height=600, width=800}) { // {{{
layers.setWidth = function(width) {
layers.width = width;
+ layers.forEach(layer => layer.canvas.setWidth(width));
easelElement.style.width = width + 2 + 'px';
}
layers.setWidth(width);
+ layers.resize = function(width, height) {
+ layers.height = height;
+ layers.width = width;
+ easelElement.style.height = height * zoom + 2 + 'px';
+ easelElement.style.width = width * zoom + 2 + 'px';
+ layers.forEach(layer => layer.canvas.resize(width, height));
+ layers[0].canvas.fill(backgroundColor);
+ }
+
+ layers.zoom = function(newZoom) {
+ easelElement.style.height = layers.height * zoom + 2 + 'px';
+ easelElement.style.width = layers.width * zoom + 2 + 'px';
+ layers.forEach(layer => {
+ layer.canvas.style.height = layers.height * zoom + 'px';
+ layer.canvas.style.width = layers.width * zoom + 'px';
+ });
+ }
+
layers.resetPosition = function() {
const studioRect = studioElement.getBoundingClientRect();
easelElement.style.left = `${studioRect.left}px`;
@@ -488,19 +614,40 @@ function makeLayers({height=600, width=800}) { // {{{
}
layers.delete = function(layer) {
- layer.canvas.deleteCanvas();
- layers.splice(layers.indexOf(layer), 1);
- layers.refresh();
+ if (!layer.background) {
+ layer.canvas.deleteCanvas();
+ layers.splice(layers.indexOf(layer), 1);
+ layers.refresh();
+ }
}
layers.deleteAll = function() {
- layers.forEach(layer => layer.deleteCanvas());
- // TODO
+ layers.forEach(function(layer) {
+ if (!layer.background) {
+ layer.canvas.deleteCanvas();
+ layers.splice(layers.indexOf(layer), 1);
+ }
+ });
}
- layers.move = function(layer, index) {
- layers.splice(layers.indexOf(layer), 1);
- layers.splice(index, 0, layer);
+ layers.moveUp = function(layer) {
+ if (layer.background) return;
+ if (layers.indexOf(layer) === layers.length - 1) return;
+ const index = layers.indexOf(layer);
+ const temp = layers[index + 1];
+ layers[index + 1] = layer;
+ layers[index] = temp;
+ layers.refresh();
+ }
+
+ layers.moveDown = function(layer) {
+ if (layer.background) return;
+ if (layers.indexOf(layer) === 1) return;
+ const index = layers.indexOf(layer);
+ const temp = layers[index - 1];
+ layers[index - 1] = layer;
+ layers[index] = temp;
+ layers.refresh();
}
layers.setActive = function(layer) {
@@ -512,6 +659,9 @@ function makeLayers({height=600, width=800}) { // {{{
return layers.find(layer => layer.active);
}
+ layers.push(makeLayer({height, width, background: true}));
+ layers[0].canvas.fill(backgroundColor);
+
return layers;
} // }}}
@@ -519,8 +669,6 @@ function makeLayers({height=600, width=800}) { // {{{
const layers = makeLayers({height: initialHeight, width: initialWidth});
layers.add();
-layers.add();
-layers[0].canvas.fill('rgb(255, 255, 255)');
layers.setActive(layers[1]);
// }}}
@@ -532,10 +680,10 @@ function makeColorPreview() {
colorPreview.element = document.createElement('div');
colorPreview.element.id = 'color-preview';
colorPreview.element.className = 'puck';
- colorPreview.element.style.backgroundColor = color.color;
+ colorPreview.element.style.backgroundColor = brushColor.toRgb();
commandBarElement.appendChild(colorPreview.element);
colorPreview.update = function() {
- colorPreview.element.style.backgroundColor = color.color;
+ colorPreview.element.style.backgroundColor = brushColor.toRgb();
}
return colorPreview;
@@ -543,6 +691,60 @@ function makeColorPreview() {
const colorPreview = makeColorPreview();
+// }}}
+
+// BRUSH PREVIEW {{{
+
+function makeBrushPreview() {
+ const brushPreview = {};
+ brushPreview.element = document.createElement('div');
+ brushPreview.element.id = 'brush-preview';
+ brushPreview.element.style.width = brushSize + 'px';
+ brushPreview.element.style.height = brushSize + 'px';
+ brushPreview.element.style.position = 'absolute';
+ brushPreview.element.style.display = 'none';
+ brushPreview.element.style.pointerEvents = 'none';
+ brushPreview.element.style.border = '1px solid black';
+ brushPreview.element.style.zIndex = '1000';
+
+ document.body.appendChild(brushPreview.element);
+
+ brushPreview.update = function() {
+ brushPreview.element.style.width = brushSize * zoom + 'px';
+ brushPreview.element.style.height = brushSize * zoom + 'px';
+ if (brushShape === 'circle') {
+ brushPreview.element.style.borderRadius = '50%';
+ } else {
+ brushPreview.element.style.borderRadius = '0';
+ }
+ if (brushSize < 3) {
+ brushPreview.element.style.visibility = 'hidden';
+ } else {
+ brushPreview.element.style.visibility = 'visible';
+ }
+ }
+
+ brushPreview.setPosition = function(x, y) {
+ brushPreview.element.style.left = x - brushSize * zoom / 2 + 'px';
+ brushPreview.element.style.top = y - brushSize * zoom / 2 + 'px';
+ }
+
+ brushPreview.show = function() {
+ brushPreview.element.style.display = 'block';
+ }
+
+ brushPreview.hide = function() {
+ brushPreview.element.style.display = 'none';
+ }
+
+ brushPreview.update();
+
+ return brushPreview;
+}
+
+const brushPreview = makeBrushPreview();
+
+
// }}}
// COMMANDS {{{
@@ -693,16 +895,28 @@ commands.add({ // change-shape {{{
func: function changeShape() {
const currentIndex = shapes.indexOf(brushShape);
brushShape = shapes[(currentIndex + 1) % shapes.length];
+ brushPreview.update();
}
}); // }}}
+commands.add({
+ name: 'reset',
+ key: 'r',
+ icon: '',
+ func: function resetCanvas() {
+ zoom = 1;
+ layers.zoom();
+ layers.resetPosition();
+ }
+});
+
// }}}
// TOOLS {{{
// FACTORY {{{
-function makeTool({name, key, icon, mouseDown, mouseMove, mouseUp}) {
+function makeTool({name, key, icon, mouseDown, mouseMove, mouseUp, mouseDrag, mouseLeave}) {
if (!name) throw new Error('No name provided');
if (!key) throw new Error('No key provided');
if (!icon) throw new Error('No icon provided');
@@ -714,6 +928,8 @@ function makeTool({name, key, icon, mouseDown, mouseMove, mouseUp}) {
tool.mouseDown = mouseDown;
tool.mouseMove = mouseMove;
tool.mouseUp = mouseUp;
+ tool.mouseDrag = mouseDrag;
+ tool.mouseLeave = mouseLeave;
tool.active = false;
tool.activate = function() {
@@ -745,8 +961,8 @@ function makeTools() {
tools.prevToolName = 'na';
- tools.add = function({name, key, icon, mouseDown, mouseMove, mouseUp}) {
- const tool = makeTool({name, key, icon, mouseDown, mouseMove, mouseUp});
+ tools.add = function({name, key, icon, mouseDown, mouseMove, mouseUp, mouseDrag, mouseLeave}) {
+ const tool = makeTool({name, key, icon, mouseDown, mouseMove, mouseUp, mouseDrag, mouseLeave});
tools.push(tool);
}
@@ -788,21 +1004,28 @@ tools.add({ // brush {{{
mouseDown: function(e) {
const canvas = layers.getActive().canvas;
if (brushSize === 1) {
- canvas.drawPixel(canvasStartX, canvasStartY, color.color);
+ canvas.drawPixel(canvasStartX, canvasStartY, brushColor.toRgb());
} else {
- canvas.drawShape(canvasStartX, canvasStartY, brushShape, brushSize, color.color);
+ canvas.drawShape(canvasStartX, canvasStartY, brushShape, brushSize, brushColor.toRgb());
}
},
mouseMove: function(e) {
+ brushPreview.show();
+ brushPreview.setPosition(e.clientX, e.clientY);
+ },
+ mouseDrag: function(e) {
const canvas = layers.getActive().canvas;
if (brushSize === 1) {
- canvas.drawLineWithPixels(canvasStartX, canvasStartY, canvasEndX, canvasEndY, color.color);
+ canvas.drawLineWithPixels(canvasStartX, canvasStartY, canvasEndX, canvasEndY, brushColor.toRgb());
} else {
- canvas.drawLineWithShape(canvasStartX, canvasStartY, canvasEndX, canvasEndY, brushShape, brushSize, color.color);
+ canvas.drawLineWithShape(canvasStartX, canvasStartY, canvasEndX, canvasEndY, brushShape, brushSize, brushColor.toRgb());
}
canvasStartX = canvasEndX;
canvasStartY = canvasEndY;
},
+ mouseLeave: function(e) {
+ brushPreview.hide();
+ }
}); // }}}
tools.add({ // content-move {{{
@@ -813,7 +1036,7 @@ tools.add({ // content-move {{{
const canvas = layers.getActive().canvas;
canvas.saveCanvas();
},
- mouseMove: function(e) {
+ mouseDrag: function(e) {
const canvas = layers.getActive().canvas;
canvas.clearCanvas();
canvas.restoreCanvas(dX, dY);
@@ -828,7 +1051,7 @@ tools.add({ // move {{{
startX = e.clientX - easelElement.offsetLeft;
startY = e.clientY - easelElement.offsetTop;
},
- mouseMove: function(e) {
+ mouseDrag: function(e) {
easelElement.style.left = dX + 'px';
easelElement.style.top = dY + 'px';
},
@@ -838,13 +1061,11 @@ tools.add({ // zoom {{{
name: 'zoom',
key: 'z',
icon: '',
- mouseMove: function(e) {
- // TODO all canvases
- // const canvas = layers.getActive().canvas;
+ mouseDrag: function(e) {
zoom += dX * dZoom;
if (zoom < 0.1) zoom = 0.1;
- // canvas.style.height = canvasHeight * zoom + 'px';
- // canvas.style.width = canvasWidth * zoom + 'px';
+ layers.zoom();
+ brushPreview.update();
startX = endX;
}
}); // }}}
@@ -855,7 +1076,7 @@ tools.add({ // bucket-fill {{{
icon: '',
mouseDown: function(e) {
const canvas = layers.getActive().canvas;
- canvas.floodFill(canvasStartX, canvasStartY, color.color);
+ canvas.floodFill(canvasStartX, canvasStartY, brushColor.toRgbaArray());
}
}); // }}}
@@ -867,7 +1088,7 @@ tools.add({ // color-picker {{{
const canvas = layers.getActive().canvas;
const imageData = canvas.ctx.getImageData(canvasStartX, canvasStartY, 1, 1).data;
const pickedColor = `rgb(${imageData[0]}, ${imageData[1]}, ${imageData[2]})`;
- color.color = pickedColor;
+ brushColor.fromRgb(pickedColor);
colorPreview.update();
}
}); // }}}
@@ -877,10 +1098,18 @@ tools.add({ // brush-size {{{
key: 'd',
icon: '',
mouseMove: function(e) {
+ brushPreview.show();
+ brushPreview.setPosition(e.clientX, e.clientY);
+ },
+ mouseDrag: function(e) {
brushSize += dX * dBrushSize;
if (brushSize < 1) brushSize = 1;
if (brushSize > maxBrushSize) brushSize = maxBrushSize;
startX = endX;
+ brushPreview.update();
+ },
+ mouseLeave: function(e) {
+ brushPreview.hide();
}
}); // }}}
@@ -888,37 +1117,39 @@ tools.add({ // resize {{{
name: 'resize',
key: 'r',
icon: '',
- mouseMove: function(e) {
- // const canvas = layers.getActive().canvas;
- // let newWidth = canvasWidth + dX / zoom;
- // let newHeight = canvasHeight + dY / zoom;
- // if (newWidth > 0 && newHeight > 0) {
- // canvas.setWidth(newWidth);
- // canvas.setHeight(newHeight);
- // canvas.style.width = newWidth * zoom + 'px';
- // canvas.style.height = newHeight * zoom + 'px';
- // canvas.ctx.clearRect(0, 0, canvas.width, canvas.height);
- // canvas.ctx.fillStyle = backgroundColor;
- // canvas.ctx.fillRect(0, 0, canvas.width, canvas.height);
- // canvas.ctx.drawImage(tempCanvas, 0, 0);
- // }
+ mouseDrag: function(e) {
+ let newWidth = layers.width + dX / zoom;
+ let newHeight = layers.height + dY / zoom;
+ layers.resize(newWidth, newHeight);
+ startX = endX;
+ startY = endY;
}
}); // }}}
-
tools.add({ // color-mix {{{
name: 'color-mix',
key: 'x',
icon: '',
mouseDown: function(e) {
- const startTime = Date.now();
- const canvas = layers.getActive().canvas;
+ tempColor.copy(canvasColor);
+ startTime = Date.now();
interval = setInterval(() => {
- let canvasColor = canvas.getColorAtPixel(canvasEndX, canvasEndX);
- console.log({canvasEndX, canvasEndY, canvasColor});
- const elapsedTime = Date.now() - startTime;
- const t = Math.min(1, elapsedTime / 10000);
- color.mix(canvasColor, t);
+ if (!tempColor.match(canvasColor)) {
+ startTime = Date.now();
+ tempColor.copy(canvasColor);
+ }
+ if (!canvasColor.isOpaque()) {
+ startTime = Date.now();
+ } else {
+ const elapsedTime = Date.now() - startTime;
+ const t = Math.min(1, elapsedTime / 10000);
+ brushColor.mixx(canvasColor, t);
+ colorPreview.update();
+ if (!isMouseDown) {
+ clearInterval(interval);
+ startTime = Date.now();
+ }
+ }
}, 50);
},
mouseUp: function(e) {
@@ -927,7 +1158,6 @@ tools.add({ // color-mix {{{
mouseLeave: function(e) {
clearInterval(interval);
}
-
}); // }}}
// }}}
@@ -963,13 +1193,13 @@ function makePuck({puckColor, key, editable=true}) {
puck.element.appendChild(keyHint);
}
-
puck.element.addEventListener('mousedown', (e) => {
const startTime = Date.now();
interval = setInterval(() => {
const elapsedTime = Date.now() - startTime;
const t = Math.min(1, elapsedTime / 10000);
- color.mix(puck.element.style.backgroundColor, t);
+ brushColor.mixxRgb(puck.element.style.backgroundColor, t);
+ colorPreview.update();
}, 50);
});
@@ -977,12 +1207,17 @@ function makePuck({puckColor, key, editable=true}) {
clearInterval(interval);
});
+ puck.element.addEventListener('mouseleave', (e) => {
+ clearInterval(interval);
+ });
+
puck.keydown = function(e) {
- const startTime = Date.now();
+ startTime = Date.now();
var interval = setInterval(() => {
const elapsedTime = Date.now() - startTime;
const t = Math.min(1, elapsedTime / 10000);
- color.mix(puck.element.style.backgroundColor, t);
+ brushColor.mixxRgb(puck.element.style.backgroundColor, t);
+ colorPreview.update();
}, 50);
function onKeyUp() {
clearInterval(interval);
@@ -1012,36 +1247,53 @@ function makePucks() {
const pucks = makePucks();
-pucks.add({
+pucks.add({ // black
puckColor: 'rgb(0, 0, 0)',
key: '1',
editable: false,
});
-pucks.add({
+pucks.add({ // white
puckColor: 'rgb(255, 255, 255)',
key: '2',
editable: false,
});
-pucks.add({
+pucks.add({ // red
puckColor: 'rgb(255, 0, 0)',
key: '3',
editable: false,
});
-pucks.add({
- puckColor: 'rgb(0, 255, 0)',
- key: '4',
+pucks.add({ // blue
+ puckColor: 'rgb(0, 0, 255)',
+ key: '5',
editable: false,
});
-pucks.add({
- puckColor: 'rgb(0, 0, 255)',
- key: '5',
+pucks.add({ // yellow
+ puckColor: 'rgb(255, 255, 0)',
+ key: '6',
+ editable: false,
+});
+
+pucks.add({ // cyan
+ puckColor: 'rgb(0, 255, 255)',
+ key: '7',
editable: false,
});
+pucks.add({ // magenta
+ puckColor: 'rgb(255, 0, 255)',
+ key: '8',
+ editable: false,
+});
+
+pucks.add({ // green
+ puckColor: 'rgb(0, 255, 0)',
+ key: '4',
+ editable: false,
+});
// }}}
@@ -1134,7 +1386,7 @@ infos.add({
infos.add({
name: 'color',
updateFunction: function() {
- return color.color;
+ return brushColor.toRgb();
}
});
@@ -1166,6 +1418,13 @@ infos.add({
}
});
+infos.add({
+ name: 'cursor-color',
+ updateFunction: function() {
+ return canvasColor;
+ }
+});
+
// }}}
// MOUSE EVENT LISTENERS {{{
@@ -1179,13 +1438,13 @@ studioElement.addEventListener('mousedown', (e) => {
canvasStartY = canvas.getPositionOnCanvas(e).y;
canvasEndX = canvas.getPositionOnCanvas(e).x;
canvasEndX = canvas.getPositionOnCanvas(e).y;
+ canvasColor.fromRgbaArray(canvas.getRgbaColorArrayAtPixel(canvasStartX, canvasStartY));
for (var i = 0; i < tools.length; i++) {
var tool = tools[i];
if (tool.active) {
if (tool.mouseDown) {
tool.mouseDown(e);
- break;
}
}
}
@@ -1204,22 +1463,23 @@ studioElement.addEventListener('mousemove', (e) => {
canvasEndY = canvas.getPositionOnCanvas(e).y;
canvasDX = canvasEndX - canvasStartX;
canvasDY = canvasEndY - canvasStartY;
+ canvasColor.fromRgbaArray(canvas.getRgbaColorArrayAtPixel(canvasEndX, canvasEndY));
- if (tools.getActive().name === 'brush-size') {
- brushPreviewElement.style.display = 'block';
- brushPreviewElement.style.width = brushSize + 'px';
- brushPreviewElement.style.height = brushSize + 'px';
- brushPreviewElement.style.left = e.clientX - brushSize / 2 + 'px';
- brushPreviewElement.style.top = e.clientY - brushSize / 2 + 'px';
+ for (var i = 0; i < tools.length; i++) {
+ var tool = tools[i];
+ if (tool.active) {
+ if (tool.mouseMove) {
+ tool.mouseMove(e);
+ }
+ }
}
if (isMouseDown) {
for (var i = 0; i < tools.length; i++) {
var tool = tools[i];
if (tool.active) {
- if (tool.mouseMove) {
- tool.mouseMove(e);
- break;
+ if (tool.mouseDrag) {
+ tool.mouseDrag(e);
}
}
}
@@ -1229,6 +1489,7 @@ studioElement.addEventListener('mousemove', (e) => {
});
+
studioElement.addEventListener('mouseup', () => {
isMouseDown = false;
@@ -1258,7 +1519,6 @@ studioElement.addEventListener('mouseleave', () => {
}
}
- brushPreviewElement.style.display = 'none';
infos.update();
});
diff --git a/temp.js b/temp.js
deleted file mode 100644
index fdb075f..0000000
--- a/temp.js
+++ /dev/null
@@ -1,724 +0,0 @@
-const colorPreview = document.createElement('div');
-colorPreview.id = 'color-preview';
-colorPreview.className = 'puck';
-colorPreview.style.backgroundColor = color;
-
-menuBar.appendChild(colorPreview);
-
-
-// }}}
-
-// helpers {{{
-
-
-function saveCanvas() {
- const dataURL = canvas.toDataURL();
- const dimensions = `${canvas.width}x${canvas.height}`;
- localStorage.setItem('mixxCanvas', dataURL);
- localStorage.setItem('mixxDimensions', dimensions);
- console.log('Canvas saved');
-}
-
-function loadCanvas() {
- const dataURL = localStorage.getItem('mixxCanvas');
- const dimensions = localStorage.getItem('mixxDimensions');
- if (dataURL && dimensions) {
- const img = new Image();
- img.src = dataURL;
- img.onload = function() {
- canvas.width = dimensions.split('x')[0];
- canvas.height = dimensions.split('x')[1];
- canvas.style.width = canvas.width * zoom + 'px';
- canvas.style.height = canvas.height * zoom + 'px';
- canvasWidth = canvas.width;
- canvasHeight = canvas.height;
- ctx.drawImage(img, 0, 0);
- }
- } else {
- console.log('No saved canvas found');
- }
-}
-
-function clearCanvasFromLocalStorage() {
- localStorage.removeItem('savedCanvas');
- localStorage.removeItem('canvasDimensions');
-}
-
-function saveState() {
- if (undoStack.length >= maxHistory) {
- undoStack.shift(); // Remove the oldest state if the stack exceeds the limit
- }
-
- undoStack.push({
- imageData: canvas.toDataURL(),
- width: canvas.width,
- height: canvas.height
- });
-
- redoStack = []; // Clear the redo stack whenever a new action is performed
-}
-
-function undo() {
- if (undoStack.length > 0) {
- const currentState = {
- imageData: canvas.toDataURL(),
- width: canvas.width,
- height: canvas.height
- };
-
- redoStack.push(currentState); // Save current state to the redo stack
- const lastState = undoStack.pop(); // Get the last state from the undo stack
-
- canvas.width = lastState.width;
- canvas.height = lastState.height;
- canvas.style.width = canvas.width * zoom + 'px';
- canvas.style.height = canvas.height * zoom + 'px';
- canvasWidth = canvas.width;
- canvasHeight = canvas.height;
-
- const img = new Image();
- img.src = lastState.imageData;
- img.onload = function() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.drawImage(img, 0, 0);
- };
- }
-}
-
-function redo() {
- if (redoStack.length > 0) {
- const currentState = {
- imageData: canvas.toDataURL(),
- width: canvas.width,
- height: canvas.height
- };
-
- undoStack.push(currentState); // Save current state to the undo stack
- const nextState = redoStack.pop(); // Get the last state from the redo stack
-
- canvas.width = nextState.width;
- canvas.height = nextState.height;
- canvas.style.width = canvas.width * zoom + 'px';
- canvas.style.height = canvas.height * zoom + 'px';
- canvasWidth = canvas.width;
- canvasHeight = canvas.height;
-
- const img = new Image();
- img.src = nextState.imageData;
- img.onload = function() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.drawImage(img, 0, 0);
- };
- }
-}
-
-function getPositionOnCanvas(e) {
- const rect = canvas.getBoundingClientRect();
- return {
- x: Math.round((e.clientX - rect.left) / zoom),
- y: Math.round((e.clientY - rect.top) / zoom),
- };
-}
-
-function drawCircle(x, y) {
- ctx.beginPath();
- ctx.arc(x, y, brushSize / 2, 0, 2 * Math.PI, false);
- ctx.fillStyle = color;
- ctx.fill();
-}
-
-function drawLineWithCircles(x1, y1, x2, y2) {
- const dx = x2 - x1;
- const dy = y2 - y1;
- const distance = Math.sqrt(dx * dx + dy * dy);
- const steps = Math.ceil(distance / (brushSize / 5));
-
- for (let i = 0; i <= steps; i++) {
- const x = x1 + (dx * i) / steps;
- const y = y1 + (dy * i) / steps;
- drawCircle(x, y);
- }
-}
-
-function saveCanvasContents() {
- tempCanvas = document.createElement('canvas');
- tempCanvas.width = canvas.width;
- tempCanvas.height = canvas.height;
- const tempCtx = tempCanvas.getContext('2d');
- tempCtx.drawImage(canvas, 0, 0);
-}
-
-function updateColorPreview() {
- colorPreview.style.backgroundColor = color;
-}
-
-function hexToRgbArray(hex) {
- if (hex.startsWith('#')) {
- hex = hex.slice(1);
- }
-
- if (hex.length === 3) {
- hex = hex.split('').map(char => char + char).join('');
- }
-
- const bigint = parseInt(hex, 16);
- return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255];
-}
-
-function floodFill(x, y, fillColor) {
- const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
- const data = imageData.data;
-
- const targetColor = getColorAtPixel(data, x, y);
- const fillColorArray = hexToRgbArray(fillColor);
-
- if (colorsMatch(targetColor, fillColorArray)) {
- return; // The clicked point is already the fill color
- }
-
- const stack = [{x, y}];
-
- while (stack.length > 0) {
- const {x, y} = stack.pop();
- const currentColor = getColorAtPixel(data, x, y);
-
- if (colorsMatch(currentColor, targetColor)) {
- setColorAtPixel(data, x, y, fillColorArray);
-
- if (x > 0) stack.push({x: x - 1, y});
- if (x < canvas.width - 1) stack.push({x: x + 1, y});
- if (y > 0) stack.push({x, y: y - 1});
- if (y < canvas.height - 1) stack.push({x, y: y + 1});
- }
- }
-
- ctx.putImageData(imageData, 0, 0);
-}
-
-function getColorAtPixel(data, x, y) {
- const index = (y * canvas.width + x) * 4;
- return [data[index], data[index + 1], data[index + 2], data[index + 3]];
-}
-
-function setColorAtPixel(data, x, y, color) {
- const index = (y * canvas.width + x) * 4;
- data[index] = color[0];
- data[index + 1] = color[1];
- data[index + 2] = color[2];
- data[index + 3] = 255; // Set alpha to fully opaque
-}
-
-function colorsMatch(a, b) {
- return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
-}
-
-// }}}
-
-// mousedown {{{
-
-canvasArea.addEventListener('mousedown', (e) => {
- if (e.target.closest('.puck')) return;
-
- startX = e.clientX;
- startY = e.clientY;
- canvasStartX = getPositionOnCanvas(e).x;
- canvasStartY = getPositionOnCanvas(e).y;
- saveCanvasContents();
- isMouseDown = true;
-
- if (
- tool === 'brush' ||
- tool === 'content-move' ||
- tool === 'resize' ||
- tool === 'zoom' ||
- tool === 'bucket-fill'
- ) {
- saveState();
- }
-
- if (tool === 'brush') {
- console.log('brush');
- drawCircle(canvasStartX, canvasStartY);
- } else if (tool === 'bucket-fill') {
- floodFill(canvasStartX, canvasStartY, color);
- return;
- } else if (tool === 'move') {
- startX = e.clientX - canvasContainer.offsetLeft;
- startY = e.clientY - canvasContainer.offsetTop;
- } else if (tool === 'color-picker') {
- const imageData = ctx.getImageData(canvasStartX, canvasStartY, 1, 1).data;
- const pickedColor = `rgb(${imageData[0]}, ${imageData[1]}, ${imageData[2]})`;
- color = pickedColor;
- console.log('Picked Color:', pickedColor);
- updateColorPreview();
- return;
- }
-});
-
-// }}}
-
-// mousemove {{{
-
-canvasArea.addEventListener('mousemove', (e) => {
-
- endX = e.clientX;
- endY = e.clientY;
- dX = endX - startX;
- dY = endY - startY;
-
- canvasEndX = getPositionOnCanvas(e).x;
- canvasEndY = getPositionOnCanvas(e).y;
- canvasDX = canvasEndX - canvasStartX;
- canvasDY = canvasEndY - canvasStartY;
-
- if (tool == 'brush-size') {
- brushPreview.style.display = 'block';
- brushPreview.style.width = brushSize + 'px';
- brushPreview.style.height = brushSize + 'px';
- brushPreview.style.left = e.clientX - brushSize / 2 + 'px';
- brushPreview.style.top = e.clientY - brushSize / 2 + 'px';
- }
-
- if (isMouseDown) {
-
- if (tool === 'brush-size') {
- brushSize += dX * dBrushSize;
- if (brushSize < 1) brushSize = 1;
- if (brushSize > maxBrushSize) brushSize = maxBrushSize;
- startX = endX;
- } else if (tool === 'brush') {
- drawLineWithCircles(canvasStartX, canvasStartY, canvasEndX, canvasEndY);
-
- canvasStartX = canvasEndX;
- canvasStartY = canvasEndY;
-
- } else if (tool === 'content-move') {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.fillStyle = backgroundColor;
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- ctx.drawImage(tempCanvas, dX, dY);
- } else if (tool === 'move') {
- canvasContainer.style.left = dX + 'px';
- canvasContainer.style.top = dY + 'px';
- } else if (tool === 'zoom') {
- zoom += dX * dZoom;
- if (zoom < 0.1) zoom = 0.1;
- canvas.style.height = canvasHeight * zoom + 'px';
- canvas.style.width = canvasWidth * zoom + 'px';
- startX = endX;
- } else if (tool === 'resize') {
- let newWidth = canvasWidth + dX / zoom;
- let newHeight = canvasHeight + dY / zoom;
- if (newWidth > 0 && newHeight > 0) {
- canvas.width = newWidth;
- canvas.height = newHeight;
- canvas.style.width = newWidth * zoom + 'px';
- canvas.style.height = newHeight * zoom + 'px';
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.fillStyle = backgroundColor;
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- ctx.drawImage(tempCanvas, 0, 0);
- }
- } else if (tool === 'color-mix') {
-
- const imageData = ctx.getImageData(canvasEndX, canvasEndY, 1, 1).data;
- const canvasColor = `rgb(${imageData[0]}, ${imageData[1]}, ${imageData[2]})`;
-
- const distance = Math.sqrt(Math.pow(e.clientX - startX, 2) + Math.pow(e.clientY - startY, 2));
- const t = Math.min(1, distance / 300);
-
- const mixedColor = mixbox.lerp(color, canvasColor, t);
-
- color = mixedColor;
-
- startX = e.clientX;
- startY = e.clientY;
-
- }
-
- }
-
- updateInfos();
- updateColorPreview();
-});
-
-// }}}
-
-// mouseup {{{
-
-canvasArea.addEventListener('mouseup', (e) => {
- isMouseDown = false;
- if (tool === 'brush') {
- ctx.closePath();
- } else if (tool === 'resize') {
- canvasWidth = canvas.width;
- canvasHeight = canvas.height;
- }
-
- updateColorPreview();
-});
-
-// }}}
-
-// mouseleave {{{
-
-canvasArea.addEventListener('mouseleave', (e) => {
- isMouseDown = false;
- brushPreview.style.display = 'none';
-});
-
-// }}}
-
-// keybindings {{{
-
-const toolKeyBindings = {}
-
-const functionKeyBindings = {
-}
-
-document.addEventListener('keydown', (e) => {
- if (keyDown) return;
-
- const newTool = toolKeyBindings[e.key.toLowerCase()]
- if (newTool) {
- prevTool = tool;
- keyDown = true;
- changeTool(newTool);
- return;
- }
-
- const func = functionKeyBindings[e.key];
- if (func) {
- func();
- return;
- }
-
-});
-
-document.addEventListener('keyup', (e) => {
- const currentTool = toolKeyBindings[e.key.toLowerCase()]
- if (currentTool) {
- keyDown = false;
- if (e.key == e.key.toLowerCase()) {
- changeTool(prevTool);
- return;
- }
- }
-});
-
-// }}}
-
-// tools {{{
-
-var toolButtons = [];
-
-function changeTool(toolName) {
- toolButtons.forEach(button => button.button.classList.remove('active'));
- toolButtons.find(button => button.name === toolName).button.classList.add('active');
- tool = toolName;
- brushPreview.style.display = 'none';
- updateInfos();
-}
-
-function createToolButton(toolName, displayName, icon, key=undefined) {
- const button = document.createElement('div');
- button.classList.add('button');
- button.classList.add('tool');
- button.innerHTML = icon;
- button.title = displayName;
- button.addEventListener('click', () => {
- changeTool(toolName);
- });
-
- if (key) {
- const keyHint = document.createElement('span');
- keyHint.className = 'key-hint';
- keyHint.innerHTML = key;
- button.appendChild(keyHint);
- toolKeyBindings[key] = toolName;
- }
-
- toolBar.appendChild(button);
- toolButtons.push({'name': toolName, 'button': button});
- return button;
-}
-
-createToolButton('brush', 'Brush', '', 'b');
-createToolButton('content-move', 'Move Content', '', 'h');
-createToolButton('move', 'Move Canvas', '', 'm');
-createToolButton('zoom', 'Zoom', '', 'z');
-createToolButton('resize', 'Resize', '', 'r');
-createToolButton('color-picker', 'Color Picker', '', 'a');
-createToolButton('color-mix', 'Color Mix', '', 'x');
-createToolButton('brush-size', 'Brush Size', '', 'd');
-createToolButton('bucket-fill', 'Bucket Fill', '', 'k');
-
-// }}}
-
-// menu functons {{{
-
-function flipCanvasHorizontally(e) {
- saveState();
- ctx.save();
- saveCanvasContents();
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.scale(-1, 1);
- ctx.translate(-canvas.width, 0);
- ctx.drawImage(tempCanvas, 0, 0);
- ctx.restore();
-}
-
-function flipCanvasVertically(e) {
- saveState();
- ctx.save();
- saveCanvasContents();
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.scale(1, -1);
- ctx.translate(0, -canvas.height);
- ctx.drawImage(tempCanvas, 0, 0);
- ctx.restore();
-}
-
-function exportCanvas(e) {
- const link = document.createElement('a');
- link.download = 'canvas.png';
- link.href = canvas.toDataURL();
- link.click();
-}
-
-function importCanvas(e) {
- const input = document.createElement('input');
- input.type = 'file';
- input.accept = 'image/*';
- input.onchange = (e) => {
- const file = e.target.files[0];
- const reader = new FileReader();
- reader.onload = (e) => {
- const img = new Image();
- img.onload = () => {
- canvas.width = img.width;
- canvas.height = img.height;
- ctx.drawImage(img, 0, 0);
- }
- img.src = e.target.result;
- }
- reader.readAsDataURL(file);
- }
- input.click();
-}
-
-function clearCanvas(e) {
- saveState();
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.fillStyle = backgroundColor;
- ctx.fillRect(0, 0, canvas.width, canvas.height);
-}
-
-function resetZoom(e) {
- zoom = 1;
- canvas.style.width = canvas.width * zoom + 'px';
- canvas.style.height = canvas.height * zoom + 'px';
- canvasWidth = canvas.width;
- canvasHeight = canvas.height;
-
- canvasAreaRect = canvasArea.getBoundingClientRect();
-
- canvasContainer.style.left = `${canvasAreaRect.left}px`;
- canvasContainer.style.top = `${canvasAreaRect.top}px`;
-}
-
-// }}}
-
-// menu {{{
-
-var menuButtons = [];
-
-function createMenuButton(icon, name, clickFunction, key=undefined) {
- const button = document.createElement('div');
- button.className = 'button';
- button.innerHTML = icon;
- button.title = name;
- if (clickFunction) {
- button.addEventListener('click', () => {
- clickFunction()
- updateInfos();
- });
- }
- menuBar.appendChild(button);
- if (key) {
- const keyHint = document.createElement('span');
- keyHint.className = 'key-hint';
- keyHint.innerHTML = key;
- button.appendChild(keyHint);
- functionKeyBindings[key] = clickFunction;
- }
- return button;
-}
-
-menuButtons.push(createMenuButton('', 'Save', saveCanvas, 's'));
-menuButtons.push(createMenuButton('', 'Load', loadCanvas));
-menuButtons.push(createMenuButton('', 'Clear', clearCanvas));
-menuButtons.push(createMenuButton('', 'Export', exportCanvas));
-menuButtons.push(createMenuButton('', 'Import', importCanvas));
-
-
-menuButtons.push(createMenuButton('', 'Flip Horizontally', flipCanvasHorizontally, 'f'));
-menuButtons.push(createMenuButton('', 'Flip Vertically', flipCanvasVertically, 'v'));
-menuButtons.push(createMenuButton('', 'Undo', undo, 'u'));
-menuButtons.push(createMenuButton('', 'Redo', redo, 'y'));
-menuButtons.push(createMenuButton('', 'Clear', clearCanvas, 'c'));
-menuButtons.push(createMenuButton('', 'Reset', resetZoom, 't'));
-menuButtons.push(createMenuButton('', 'Add Color', createPuck));
-
-// }}}
-
-// pucks {{{
-
-function createPuck(c, editable=true, key=undefined) {
- if (c === undefined) {
- c = color;
- }
-
- const puck = document.createElement('div');
- puck.className = 'puck';
- puck.style.backgroundColor = c;
-
- // const selectHandle = document.createElement('div');
- // selectHandle.className = 'select-handle';
- // selectHandle.innerHTML = '';
- // puck.appendChild(selectHandle);
-
- // selectHandle.addEventListener('click', () => {
- // color = puck.style.backgroundColor;
- // updateColorPreview();
- // updateInfos();
- // });
-
- if (editable) {
- // const updateHandle = document.createElement('div');
- // updateHandle.className = 'update-handle';
- // updateHandle.innerHTML = '';
- // puck.appendChild(updateHandle);
-
- // updateHandle.addEventListener('click', () => {
- // puck.style.backgroundColor = color;
- // });
-
- const deleteHandle = document.createElement('div');
- deleteHandle.className = 'delete-handle';
- deleteHandle.innerHTML = '';
- puck.appendChild(deleteHandle);
-
- deleteHandle.addEventListener('click', () => {
- console.log("test");
- puck.remove();
- });
- }
-
- if (key) {
- const keyHint = document.createElement('div');
- keyHint.className = 'key-hint';
- keyHint.innerHTML = key;
- puck.appendChild(keyHint);
- }
-
- function mixx(startTime) {
- var interval = setInterval(() => {
- const elapsedTime = Date.now() - startTime;
- const t = Math.min(1, elapsedTime / 10000);
- const mixedColor = mixbox.lerp(color, puck.style.backgroundColor, t);
- color = mixedColor;
- updateColorPreview();
- updateInfos();
- }, 50);
- return interval;
- }
-
- puck.addEventListener('mousedown', (e) => {
- const startTime = Date.now();
- var interval = mixx(startTime);
- function onMouseUp() {
- clearInterval(interval);
- document.removeEventListener('mouseup', onMouseUp);
- }
- document.addEventListener('mouseup', onMouseUp);
- });
-
- document.addEventListener('keydown', (e) => {
- if (e.key == key) {
- console.log(e.key);
- const startTime = Date.now();
- var interval = mixx(startTime);
- function onKeyUp() {
- clearInterval(interval);
- document.removeEventListener('keyup', onKeyUp);
- }
- document.addEventListener('keyup', onKeyUp);
- }
- });
-
- menuBar.appendChild(puck);
-}
-
-createPuck(c='rgb(0, 0, 0)', editable=false, key='1');
-createPuck(c='rgb(255, 0, 0)', editale=false, key='2');
-createPuck(c='rgb(0, 0, 255)', editale=false, key='3');
-createPuck(c='rgb(255, 255, 0)', editale=false, key='4');
-createPuck(c='rgb(99, 60, 22)', editale=false, key='5');
-createPuck(c='rgb(0, 255, 0)', editale=false, key='6');
-createPuck(c='rgb(255, 0, 255)', editale=false, key='7');
-createPuck(c='rgb(0, 255, 255)', editale=false, key='8');
-createPuck(c='rgb(255, 255, 255)', editale=false, key='9');
-
-
-// }}}
-
-// info {{{
-
-var infos = [];
-
-function createInfo(name, updateFunction) {
- const info = document.createElement('span');
- info.className = 'info';
- const key = document.createElement('span');
- key.className = 'key';
- key.innerHTML = name + ':';
- const value = document.createElement('span');
- value.className = 'value';
- value.innerHTML = '0';
- info.appendChild(key);
- info.appendChild(value);
- infoBar.appendChild(info);
- function update() {
- let v = updateFunction();
- if (v === undefined) v = '?';
- value.innerHTML = v;
- }
- return update;
-}
-
-infos.push(createInfo('zoom', function() {
- var percent = zoom * 100;
- return percent.toFixed(0) + '%';
-}));
-infos.push(createInfo('brush', function() { return brushSize; }));
-infos.push(createInfo('x', function() { return canvasEndX; }));
-infos.push(createInfo('y', function() { return canvasEndY; }));
-infos.push(createInfo('color', function() { return color; }));
-infos.push(createInfo('width', function() { return canvas.width; }));
-infos.push(createInfo('height', function() { return canvas.height; }));
-
-function updateInfos() {
- infos.forEach(info => info());
-}
-
-// }}}
-
-// start {{{
-
-ctx.fillStyle = backgroundColor;
-ctx.fillRect(0, 0, canvas.width, canvas.height);
-updateInfos();
-toolButtons[0]['button'].click();
-
-// }}}