diff --git a/archive/temp.js b/archive/temp.js
index 883081d..5076e70 100644
--- a/archive/temp.js
+++ b/archive/temp.js
@@ -37,7 +37,6 @@ function createPuck(c) {
dragHandle.innerHTML = '';
puckMenu.appendChild(dragHandle);
-
well.addEventListener('mousedown', (e) => {
let isMixing = true;
let startX = e.clientX;
diff --git a/render.js b/render.js
index bfd217e..aadbb71 100644
--- a/render.js
+++ b/render.js
@@ -9,12 +9,11 @@ const canvasContainer = document.getElementById('canvas-container');
const brushPreview = document.getElementById('brush-preview');
const canvas = document.getElementById('canvas');
+// canvas.style.imageRendering = 'pixelated';
const ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
-ctx.fillStyle = 'white';
-ctx.fillRect(0, 0, canvas.width, canvas.height);
canvas.width = 800;
canvas.height = 600;
@@ -31,6 +30,7 @@ let zoom = 1;
let brushSize = 5;
let dBrushSize = 0.5;
let maxBrushSize = 500;
+let backgroundColor = 'rgb(255, 255, 255)';
let color = 'rgb(0, 0, 0)';
let tool
@@ -247,7 +247,7 @@ canvasArea.addEventListener('mousedown', (e) => {
isMouseDown = true;
if (
- tool === 'draw' ||
+ tool === 'brush' ||
tool === 'content-move' ||
tool === 'resize' ||
tool === 'zoom' ||
@@ -256,7 +256,7 @@ canvasArea.addEventListener('mousedown', (e) => {
saveState();
}
- if (tool === 'draw') {
+ if (tool === 'brush') {
drawCircle(canvasStartX, canvasStartY);
} else if (tool === 'bucket-fill') {
floodFill(canvasStartX, canvasStartY, color);
@@ -305,7 +305,7 @@ canvasArea.addEventListener('mousemove', (e) => {
if (brushSize < 1) brushSize = 1;
if (brushSize > maxBrushSize) brushSize = maxBrushSize;
startX = endX;
- } else if (tool === 'draw') {
+ } else if (tool === 'brush') {
drawLineWithCircles(canvasStartX, canvasStartY, canvasEndX, canvasEndY);
canvasStartX = canvasEndX;
@@ -313,6 +313,8 @@ canvasArea.addEventListener('mousemove', (e) => {
} 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';
@@ -332,6 +334,8 @@ canvasArea.addEventListener('mousemove', (e) => {
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') {
@@ -362,7 +366,7 @@ canvasArea.addEventListener('mousemove', (e) => {
canvasArea.addEventListener('mouseup', (e) => {
isMouseDown = false;
- if (tool === 'draw') {
+ if (tool === 'brush') {
ctx.closePath();
} else if (tool === 'resize') {
canvasWidth = canvas.width;
@@ -377,6 +381,7 @@ canvasArea.addEventListener('mouseup', (e) => {
// mouseleave {{{
canvasArea.addEventListener('mouseleave', (e) => {
+ isMouseDown = false;
brushPreview.style.display = 'none';
});
@@ -386,32 +391,49 @@ canvasArea.addEventListener('mouseleave', (e) => {
var toolButtons = [];
-function createToolButton(icon, buttonTool) {
+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(displayName, icon, toolName, jumpKey=undefined, temporaryKey=undefined) {
const button = document.createElement('div');
button.classList.add('button');
button.classList.add('tool');
button.innerHTML = icon;
- button.title = buttonTool;
+ button.title = displayName;
button.addEventListener('click', () => {
- buttons = document.getElementsByClassName('tool-button');
- toolButtons.forEach(button => button.button.classList.remove('active'));
- button.classList.add('active');
- tool = buttonTool;
- updateInfos();
+ changeTool(toolName);
});
+ if (jumpKey) {
+ const jumpKeyHint = document.createElement('span');
+ jumpKeyHint.className = 'jump-key-hint';
+ jumpKeyHint.innerHTML = jumpKey;
+ button.appendChild(jumpKeyHint);
+ }
+ if (temporaryKey) {
+ const temporaryKeyHint = document.createElement('span');
+ temporaryKeyHint.className = 'temporary-key-hint';
+ temporaryKeyHint.innerHTML = temporaryKey;
+ button.appendChild(temporaryKeyHint);
+ }
+
toolBar.appendChild(button);
return button;
}
-toolButtons.push({'name': 'draw', 'button': createToolButton('', 'draw')});
-toolButtons.push({'name': 'content-move', 'button': createToolButton('', 'content-move')});
-toolButtons.push({'name': 'move', 'button': createToolButton('', 'move')});
-toolButtons.push({'name': 'zoom', 'button': createToolButton('', 'zoom')});
-toolButtons.push({'name': 'resize', 'button': createToolButton('', 'resize')});
-toolButtons.push({'name': 'color-picker', 'button': createToolButton('', 'color-picker')});
-toolButtons.push({'name': 'color-mix', 'button': createToolButton('', 'color-mix')});
-toolButtons.push({'name': 'brush-size', 'button': createToolButton('', 'brush-size')});
-toolButtons.push({'name': 'bucket-fill', 'button': createToolButton('', 'bucket-fill')});
+toolButtons.push({'name': 'brush', 'button': createToolButton('Brush', '', 'brush', 'e', undefined)});
+toolButtons.push({'name': 'content-move', 'button': createToolButton('Move Content', '', 'content-move', 'h', undefined)});
+toolButtons.push({'name': 'move', 'button': createToolButton('Move Canvas', '', 'move', 'm', undefined)});
+toolButtons.push({'name': 'zoom', 'button': createToolButton('Zoom', '', 'zoom', 'z', undefined)});
+toolButtons.push({'name': 'resize', 'button': createToolButton('Resize', '', 'resize', 'r', undefined)});
+toolButtons.push({'name': 'color-picker', 'button': createToolButton('Color Picker', '', 'color-picker', 'a', undefined)});
+toolButtons.push({'name': 'color-mix', 'button': createToolButton('Color Mix', '', 'color-mix', 's', undefined)});
+toolButtons.push({'name': 'brush-size', 'button': createToolButton('Brush Size', '', 'brush-size', 'd', undefined)});
+toolButtons.push({'name': 'bucket-fill', 'button': createToolButton('Bucket Fill', '', 'bucket-fill', 'f', undefined)});
// }}}
@@ -497,8 +519,8 @@ function createMenuButton(icon, name, clickFunction) {
button.innerHTML = icon;
button.title = name;
if (clickFunction) {
- button.addEventListener('click', (e) => {
- clickFunction(e)
+ button.addEventListener('click', () => {
+ clickFunction()
updateInfos();
});
}
@@ -513,34 +535,127 @@ menuButtons.push(createMenuButton('', 'Flip V
menuButtons.push(createMenuButton('', 'Undo', undo));
menuButtons.push(createMenuButton('', 'Redo', redo));
menuButtons.push(createMenuButton('', 'Clear', clearCanvas));
-menuButtons.push(createMenuButton('', 'Reset Zoom', resetZoom));
+menuButtons.push(createMenuButton('', 'Reset', resetZoom));
+menuButtons.push(createMenuButton('', 'Add Color', createPuck));
// }}}
// pucks {{{
-function createPuck(c) {
+function createPuck(c, editable=true) {
+ if (c === undefined) {
+ c = color;
+ }
+
const puck = document.createElement('div');
puck.className = 'puck';
puck.style.backgroundColor = c;
- puck.addEventListener('click', () => {
- color = 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();
+ });
+ }
+
+ puck.addEventListener('mousedown', (e) => {
+ let isMixing = true;
+ const startTime = Date.now(); // Record the time when the mouse is pressed
+
+ // Interval to update the color based on time
+ const interval = setInterval(() => {
+ if (isMixing) {
+ 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); // Update every 50ms
+
+ document.addEventListener('mouseup', onMouseUp);
+
+ function onMouseUp() {
+ isMixing = false;
+ clearInterval(interval); // Stop the interval when the mouse is released
+ document.removeEventListener('mouseup', onMouseUp);
+ }
+ });
+
+ // puck.addEventListener('mousedown', (e) => {
+ // let isMixing = true;
+ // let startX = e.clientX;
+ // let startY = e.clientY;
+
+ // document.addEventListener('mousemove', onMouseMove);
+ // document.addEventListener('mouseup', onMouseUp);
+
+ // function onMouseMove(e) {
+ // if (isMixing) {
+ // 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, puck.style.backgroundColor, t);
+
+ // color = mixedColor;
+
+ // startX = e.clientX;
+ // startY = e.clientY;
+ // updateColorPreview();
+ // updateInfos();
+ // }
+ // }
+
+ // function onMouseUp() {
+ // isMixing = false;
+ // document.removeEventListener('mousemove', onMouseMove);
+ // document.removeEventListener('mouseup', onMouseUp);
+ // }
+ // });
+
+
menuBar.appendChild(puck);
}
-createPuck('rgb(255, 0, 0)');
-createPuck('rgb(0, 255, 0)');
-createPuck('rgb(0, 0, 255)');
-createPuck('rgb(255, 255, 0)');
-createPuck('rgb(255, 0, 255)');
-createPuck('rgb(0, 255, 255)');
-createPuck('rgb(0, 0, 0)');
-createPuck('rgb(255, 255, 255)');
+createPuck(c='rgb(0, 0, 0)', editable=false);
+createPuck(c='rgb(255, 255, 255)', editale=false);
+createPuck(c='rgb(0, 255, 0)', editale=false);
+createPuck(c='rgb(0, 0, 255)', editale=false);
+createPuck(c='rgb(255, 255, 0)', editale=false);
+createPuck(c='rgb(255, 0, 0)', editale=false);
+createPuck(c='rgb(255, 0, 255)', editale=false);
+createPuck(c='rgb(0, 255, 255)', editale=false);
+
// }}}
@@ -568,7 +683,6 @@ function createInfo(name, updateFunction) {
return update;
}
-infos.push(createInfo('tool', function() { return tool; }));
infos.push(createInfo('zoom', function() {
var percent = zoom * 100;
return percent.toFixed(0) + '%';
@@ -586,10 +700,59 @@ function updateInfos() {
// }}}
+// keybindings {{{
+
+let keyDown = false;
+let oldTool = tool;
+
+const toolBindings = [
+ {'key': 'e', 'tool': 'brush', 'persistent': true},
+ {'key': 'h', 'tool': 'content-move', 'persistent': true},
+ {'key': 'm', 'tool': 'move', 'persistent': true},
+ {'key': 'z', 'tool': 'zoom', 'persistent': true},
+ {'key': 'r', 'tool': 'resize', 'persistent': true},
+ {'key': 'a', 'tool': 'color-picker', 'persistent': false},
+ {'key': 's', 'tool': 'color-mix', 'persistent': false},
+ {'key': 'd', 'tool': 'brush-size', 'persistent': false},
+]
+
+const functionBindings = [
+ {'key': 'u', 'function': undo},
+ {'key': 'y', 'function': redo},
+ {'key': 'backspace', 'function': clearCanvas},
+]
+
+document.addEventListener('keydown', (e) => {
+ if (keyDown) return;
+
+ if (toolBindings.map(b => b.key).includes(e.key)) {
+ oldTool = tool;
+ keyDown = true;
+ changeTool(toolBindings.find(b => b.key === e.key).tool);
+ return;
+ }
+
+ if (functionBindings.map(b => b.key).includes(e.key)) {
+ functionBindings.find(b => b.key === e.key).function();
+ }
+});
+
+document.addEventListener('keyup', (e) => {
+ keyDown = false;
+ if (toolBindings.filter(b => !b.persistent).map(b => b.key).includes(e.key)) {
+ changeTool(oldTool);
+ }
+});
+
+// }}}
+
// start {{{
+ctx.fillStyle = backgroundColor;
+ctx.fillRect(0, 0, canvas.width, canvas.height);
updateInfos();
toolButtons[0]['button'].click();
+resetZoom();
// }}}
diff --git a/style.css b/style.css
index 27cf88a..0662bbb 100644
--- a/style.css
+++ b/style.css
@@ -15,6 +15,18 @@ body {
height: 100vh;
width: 100vw;
font-family: 'VT323', monospace;
+ background-color: darkgray;
+}
+
+#menu-bar,
+#info-bar,
+#tool-bar,
+#layer-bar {
+ z-index: 2;
+}
+
+#menu-bar {
+ justify-content: space-between;
}
#menu-bar, #info-bar {
@@ -24,6 +36,7 @@ body {
flex-direction: row;
gap: 10px;
justify-content: flex-start;
+ flex-wrap: wrap;
}
#menu-bar {
@@ -53,7 +66,6 @@ body {
}
#canvas-area {
- background-color: darkgray;
flex-grow: 1;
height: 100%;
border: 1px solid;
@@ -63,7 +75,7 @@ body {
#canvas-container {
position: absolute;
border: 1px solid;
- background-color: white;
+ z-index: -1;
}
@@ -72,6 +84,8 @@ body {
}
.button {
+ position: relative;
+ flex-shrink: 0;
background-color: #ddd;
border: 1px solid;
border-radius: 2px;
@@ -82,15 +96,92 @@ body {
flex-direction: column;
text-align: center;
justify-content: center;
+ cursor: pointer;
}
.puck {
+ position: relative;
+ flex-shrink: 0;
width: 30px;
height: 30px;
border: 1px solid;
border-radius: 2px;
}
+.select-handle {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ font-size: .5em;
+ background-color: black;
+ color: white;
+ padding: 3px 0 1px 4px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ border-radius: 10px 0 0 0;
+ border-left: 1px solid white;
+ border-top: 1px solid white;
+ cursor: pointer;
+}
+
+.update-handle {
+ position: absolute;
+ top: 0;
+ right: 0;
+ font-size: .5em;
+ background-color: black;
+ color: white;
+ padding: 1px 0 3px 4px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ border-radius: 0 0 0 10px;
+ border-left: 1px solid white;
+ border-bottom: 1px solid white;
+ cursor: pointer;
+}
+
+.delete-handle {
+ position: absolute;
+ top: 0;
+ left: 0;
+ font-size: .5em;
+ background-color: black;
+ color: white;
+ padding: 1px 4px 3px 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ border-radius: 0 0 10px 0;
+ border-right: 1px solid white;
+ border-bottom: 1px solid white;
+ cursor: pointer;
+}
+
+.jump-key-hint {
+ display: block;
+ height: 12px;
+ width: 8px;
+ line-height: 8px;
+ /* width: 12px; */
+ position: absolute;
+ top: 0;
+ left: 0;
+ font-size: 1em;
+ /* padding: 2px 2px 2px 2px; */
+ /* display: flex; */
+ /* align-items: center; */
+ justify-content: center;
+ text-align: center;
+ border-radius: 0 0 5px 0;
+ /* border-right: 1px solid; */
+ /* border-bottom: 1px solid; */
+}
+
.button.active, .button:active {
background-color: darkgray;
}