Browse Source

lazygit

main
Gregory Leeman 7 months ago
parent
commit
e453aec077
  1. 0
      archive/cool.js
  2. 0
      archive/history.js
  3. 25
      archive/temp.css
  4. 762
      archive/temp.js
  5. 0
      archive/temp2.js
  6. 510
      render.js
  7. 724
      temp.js

0
cool.js → archive/cool.js

0
history.js → archive/history.js

25
archive/temp.css

@ -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;
}

762
archive/temp.js

@ -1,124 +1,724 @@
function createPuck(c) { const colorPreview = document.createElement('div');
const puck = document.createElement('div'); colorPreview.id = 'color-preview';
puck.className = 'puck'; colorPreview.className = 'puck';
colorPreview.style.backgroundColor = color;
const well = document.createElement('div'); menuBar.appendChild(colorPreview);
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 = '<i class="fa-solid fa-droplet"></i>';
puckMenu.appendChild(selectHandle);
const copyHandle = document.createElement('div'); // helpers {{{
copyHandle.className = 'copy-handle';
copyHandle.innerHTML = '<i class="fa-regular fa-copy"></i>';
puckMenu.appendChild(copyHandle);
const deleteHandle = document.createElement('div');
deleteHandle.className = 'delete-handle';
deleteHandle.innerHTML = '<i class="fa-solid fa-trash-can"></i>';
puckMenu.appendChild(deleteHandle);
const updateHandle = document.createElement('div'); function saveCanvas() {
updateHandle.className = 'update-handle'; const dataURL = canvas.toDataURL();
updateHandle.innerHTML = '<i class="fa-solid fa-fill"></i>'; const dimensions = `${canvas.width}x${canvas.height}`;
puckMenu.appendChild(updateHandle); 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 dragHandle = document.createElement('div'); function clearCanvasFromLocalStorage() {
dragHandle.className = 'drag-handle'; localStorage.removeItem('savedCanvas');
dragHandle.innerHTML = '<i class="fa-solid fa-bars"></i>'; localStorage.removeItem('canvasDimensions');
puckMenu.appendChild(dragHandle); }
well.addEventListener('mousedown', (e) => { function saveState() {
let isMixing = true; if (undoStack.length >= maxHistory) {
let startX = e.clientX; undoStack.shift(); // Remove the oldest state if the stack exceeds the limit
let startY = e.clientY; }
document.addEventListener('mousemove', onMouseMove); undoStack.push({
document.addEventListener('mouseup', onMouseUp); imageData: canvas.toDataURL(),
width: canvas.width,
height: canvas.height
});
function onMouseMove(e) { redoStack = []; // Clear the redo stack whenever a new action is performed
if (isMixing) { }
const distance = Math.sqrt(Math.pow(e.clientX - startX, 2) + Math.pow(e.clientY - startY, 2));
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 t = Math.min(1, distance / 300);
const mixedColor = mixbox.lerp(color, well.style.backgroundColor, t); const mixedColor = mixbox.lerp(color, canvasColor, t);
color = mixedColor; color = mixedColor;
startX = e.clientX; startX = e.clientX;
startY = e.clientY; startY = e.clientY;
} }
} }
function onMouseUp() { updateInfos();
isMixing = false; updateColorPreview();
document.removeEventListener('mousemove', onMouseMove); });
document.removeEventListener('mouseup', onMouseUp);
// }}}
// mouseup {{{
canvasArea.addEventListener('mouseup', (e) => {
isMouseDown = false;
if (tool === 'brush') {
ctx.closePath();
} else if (tool === 'resize') {
canvasWidth = canvas.width;
canvasHeight = canvas.height;
} }
});
dragHandle.addEventListener('mousedown', (e) => { updateColorPreview();
let isMovingPuck = true; });
let startX = e.clientX;
let startY = e.clientY; // }}}
let left = puck.offsetLeft;
let top = puck.offsetTop; // mouseleave {{{
document.addEventListener('mousemove', (e) => {
if (isMovingPuck) { canvasArea.addEventListener('mouseleave', (e) => {
puck.style.left = left + e.clientX - startX + 'px'; isMouseDown = false;
puck.style.top = top + e.clientY - startY + 'px'; 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);
}); });
document.addEventListener('mouseup', () => {
isMovingPuck = false; if (key) {
document.removeEventListener('mousemove', () => {}); const keyHint = document.createElement('span');
document.removeEventListener('mouseup', () => {}); 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', '<i class="fa-solid fa-paintbrush"></i>', 'b');
createToolButton('content-move', 'Move Content', '<i class="fa-regular fa-hand"></i>', 'h');
createToolButton('move', 'Move Canvas', '<i class="fa-solid fa-arrows-up-down-left-right"></i>', 'm');
createToolButton('zoom', 'Zoom', '<i class="fa-solid fa-magnifying-glass"></i>', 'z');
createToolButton('resize', 'Resize', '<i class="fa-solid fa-ruler-combined"></i>', 'r');
createToolButton('color-picker', 'Color Picker', '<i class="fa-solid fa-eye-dropper"></i>', 'a');
createToolButton('color-mix', 'Color Mix', '<i class="fa-solid fa-mortar-pestle"></i>', 'x');
createToolButton('brush-size', 'Brush Size', '<i class="fa-regular fa-circle-dot"></i>', 'd');
createToolButton('bucket-fill', 'Bucket Fill', '<i class="fa-solid fa-fill"></i>', '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('<i class="fa-solid fa-download"></i>', 'Save', saveCanvas, 's'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-upload"></i>', 'Load', loadCanvas));
menuButtons.push(createMenuButton('<i class="fa-solid fa-trash-can"></i>', 'Clear', clearCanvas));
menuButtons.push(createMenuButton('<i class="fa-solid fa-floppy-disk"></i>', 'Export', exportCanvas));
menuButtons.push(createMenuButton('<i class="fa-regular fa-folder-open"></i>', 'Import', importCanvas));
menuButtons.push(createMenuButton('<i class="fa-solid fa-left-right"></i>', 'Flip Horizontally', flipCanvasHorizontally, 'f'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-up-down"></i>', 'Flip Vertically', flipCanvasVertically, 'v'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-undo"></i>', 'Undo', undo, 'u'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-redo"></i>', 'Redo', redo, 'y'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-pump-soap"></i>', 'Clear', clearCanvas, 'c'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-house"></i>', 'Reset', resetZoom, 't'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-plus"></i>', 'Add Color', createPuck));
// }}}
// pucks {{{
function createPuck(c, editable=true, key=undefined) {
if (c === undefined) {
c = color;
}
updateHandle.addEventListener('click', () => { const puck = document.createElement('div');
console.log('update'); puck.className = 'puck';
well.style.backgroundColor = color; puck.style.backgroundColor = c;
// const selectHandle = document.createElement('div');
// selectHandle.className = 'select-handle';
// selectHandle.innerHTML = '<i class="fa-solid fa-droplet"></i>';
// 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 = '<i class="fa-solid fa-fill"></i>';
// puck.appendChild(updateHandle);
// updateHandle.addEventListener('click', () => {
// puck.style.backgroundColor = color;
// });
const deleteHandle = document.createElement('div');
deleteHandle.className = 'delete-handle';
deleteHandle.innerHTML = '<i class="fa-solid fa-trash-can"></i>';
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);
}
selectHandle.addEventListener('click', () => { function mixx(startTime) {
console.log('select'); var interval = setInterval(() => {
color = well.style.backgroundColor; const elapsedTime = Date.now() - startTime;
console.log(color); const t = Math.min(1, elapsedTime / 10000);
const mixedColor = mixbox.lerp(color, puck.style.backgroundColor, t);
color = mixedColor;
updateColorPreview(); updateColorPreview();
}); updateInfos();
}, 50);
return interval;
}
copyHandle.addEventListener('click', () => { puck.addEventListener('mousedown', (e) => {
pucks.push(createPuck(well.style.backgroundColor)); const startTime = Date.now();
var interval = mixx(startTime);
function onMouseUp() {
clearInterval(interval);
document.removeEventListener('mouseup', onMouseUp);
}
document.addEventListener('mouseup', onMouseUp);
}); });
deleteHandle.addEventListener('click', () => { document.addEventListener('keydown', (e) => {
pucks = pucks.filter(p => p !== puck); if (e.key == key) {
puck.remove(); 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; // info {{{
let canvasHeight = canvasArea.offsetHeight;
let randonX = Math.floor(Math.random() * canvasWidth); var infos = [];
let randonY = Math.floor(Math.random() * canvasHeight);
puck.style.left = randonX + 'px'; function createInfo(name, updateFunction) {
puck.style.top = randonY + 'px'; 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; }));
return puck; function updateInfos() {
infos.forEach(info => info());
} }
// }}}
// start {{{
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
updateInfos();
toolButtons[0]['button'].click();
// }}}

0
temp2.js → archive/temp2.js

510
render.js

@ -6,7 +6,6 @@ const layerControllersElement = document.getElementById('layer-controllers');
const studioElement = document.getElementById('studio'); const studioElement = document.getElementById('studio');
const infoBarElement = document.getElementById('info-bar'); const infoBarElement = document.getElementById('info-bar');
const easelElement = document.getElementById('easel'); const easelElement = document.getElementById('easel');
const brushPreviewElement = document.getElementById('brush-preview');
const dZoom = 0.001; const dZoom = 0.001;
const dBrushSize = 0.5; const dBrushSize = 0.5;
@ -41,6 +40,7 @@ let isKeyDown = false;
let isMouseDown = false; let isMouseDown = false;
let interval; let interval;
var startTime;
// }}} // }}}
@ -56,15 +56,14 @@ function disableImageSmoothing(ctx) {
}; };
function hexToRgbArray(hex) { function hexToRgbArray(hex) {
hex = hex.replace(/^#/, ''); const r = parseInt(hex.substring(1, 3), 16);
const bigint = parseInt(hex, 16); const g = parseInt(hex.substring(3, 5), 16);
const r = (bigint >> 16) & 255; const b = parseInt(hex.substring(5, 7), 16);
const g = (bigint >> 8) & 255; return [r, g, b, 255];
const b = bigint & 255;
return [r, g, b, 255]; // Add 255 for full opacity
} }
function colorsMatch(color1, color2, tolerance = 0) { 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 && return Math.abs(color1[0] - color2[0]) <= tolerance &&
Math.abs(color1[1] - color2[1]) <= tolerance && Math.abs(color1[1] - color2[1]) <= tolerance &&
Math.abs(color1[2] - color2[2]) <= tolerance && Math.abs(color1[2] - color2[2]) <= tolerance &&
@ -78,6 +77,10 @@ function makeIconElement(htmlString) {
return iconElement; 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}) { function makeButtonElement({icon, name, func, key}) {
if (!icon) throw new Error('No icon provided'); if (!icon) throw new Error('No icon provided');
if (!name) throw new Error('No name provided'); if (!name) throw new Error('No name provided');
@ -118,10 +121,33 @@ function makeButtonElement({icon, name, func, key}) {
function makeColor(rgb) { function makeColor(rgb) {
const color = {}; 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() { 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() { color.toHex = function() {
@ -131,17 +157,95 @@ function makeColor(rgb) {
return `#${color.r.toString(16)}${color.g.toString(16)}${color.b.toString(16)}`; return `#${color.r.toString(16)}${color.g.toString(16)}${color.b.toString(16)}`;
} }
color.mix = function(color2, t) { color.fromRgbaArray = function(array) {
const color1 = color.color; color.r = array[0];
const newColor = mixbox.lerp(color1, color2, t); color.g = array[1];
color.color = newColor; color.b = array[2];
colorPreview.update(); 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; 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 {{{ // FACTORY {{{
function makeCanvas({height=600, width=800}) { // {{{ function makeCanvas({height=600, width=800, background=false}) { // {{{
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
canvas.style.imageRendering = 'pixelated'; canvas.style.imageRendering = 'pixelated';
canvas.ctx = canvas.getContext('2d'); canvas.ctx = canvas.getContext('2d');
canvas.background = background;
canvas.tempCanvas = document.createElement('canvas'); canvas.tempCanvas = document.createElement('canvas');
canvas.tempCtx = canvas.tempCanvas.getContext('2d'); canvas.tempCtx = canvas.tempCanvas.getContext('2d');
@ -167,8 +272,10 @@ function makeCanvas({height=600, width=800}) { // {{{
} }
canvas.clearCanvas = function() { canvas.clearCanvas = function() {
if (!canvas.background) {
canvas.ctx.clearRect(0, 0, canvas.width, canvas.height); canvas.ctx.clearRect(0, 0, canvas.width, canvas.height);
} }
}
canvas.restoreCanvas = function(x=0, y=0) { canvas.restoreCanvas = function(x=0, y=0) {
canvas.ctx.drawImage(canvas.tempCanvas, x, y); canvas.ctx.drawImage(canvas.tempCanvas, x, y);
@ -185,9 +292,14 @@ function makeCanvas({height=600, width=800}) { // {{{
}; };
canvas.resize = function(width, height) { canvas.resize = function(width, height) {
canvas.saveCanvas();
canvas.clearCanvas();
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
canvas.style.width = width * zoom + 'px';
canvas.style.height = height * zoom + 'px';
disableImageSmoothing(canvas.ctx); disableImageSmoothing(canvas.ctx);
canvas.restoreCanvas();
} }
canvas.getPositionOnCanvas = function(e) { canvas.getPositionOnCanvas = function(e) {
@ -199,11 +311,14 @@ function makeCanvas({height=600, width=800}) { // {{{
} }
canvas.drawPixel = function(x, y, color) { canvas.drawPixel = function(x, y, color) {
if (!canvas.background) {
canvas.ctx.fillStyle = color; canvas.ctx.fillStyle = color;
canvas.ctx.fillRect(x, y, 1, 1); canvas.ctx.fillRect(x, y, 1, 1);
} }
}
canvas.drawLineWithPixels = function(x1, y1, x2, y2, color) { canvas.drawLineWithPixels = function(x1, y1, x2, y2, color) {
if (!canvas.background) {
const dx = Math.abs(x2 - x1); const dx = Math.abs(x2 - x1);
const dy = Math.abs(y2 - y1); const dy = Math.abs(y2 - y1);
const sx = x1 < x2 ? 1 : -1; const sx = x1 < x2 ? 1 : -1;
@ -217,8 +332,10 @@ function makeCanvas({height=600, width=800}) { // {{{
if (e2 < dx) { err += dx; y1 += sy; } if (e2 < dx) { err += dx; y1 += sy; }
} }
} }
}
canvas.drawShape = function(x, y, shape, size, color) { canvas.drawShape = function(x, y, shape, size, color) {
if (!canvas.background) {
x = Math.round(x); x = Math.round(x);
y = Math.round(y); y = Math.round(y);
@ -231,7 +348,7 @@ function makeCanvas({height=600, width=800}) { // {{{
if (shape === 'square') { if (shape === 'square') {
canvas.ctx.fillRect(x - Math.floor(size / 2), y - Math.floor(size / 2), size, size); canvas.ctx.fillRect(x - Math.floor(size / 2), y - Math.floor(size / 2), size, size);
} else if (shape === 'circle') { } else if (shape === 'circle') {
let radius = Math.floor(size / 1); let radius = Math.floor(size / 2);
let radiusSquared = radius * radius; let radiusSquared = radius * radius;
for (let y1 = -radius; y1 <= radius; y1++) { for (let y1 = -radius; y1 <= radius; y1++) {
@ -269,8 +386,10 @@ function makeCanvas({height=600, width=800}) { // {{{
} }
} }
} }
}
canvas.drawLineWithShape = function(x1, y1, x2, y2, shape, size, color) { canvas.drawLineWithShape = function(x1, y1, x2, y2, shape, size, color) {
if (!canvas.background) {
const dx = x2 - x1; const dx = x2 - x1;
const dy = y2 - y1; const dy = y2 - y1;
const distance = Math.sqrt(dx * dx + dy * dy); const distance = Math.sqrt(dx * dx + dy * dy);
@ -282,19 +401,20 @@ function makeCanvas({height=600, width=800}) { // {{{
canvas.drawShape(x, y, shape, size, color); canvas.drawShape(x, y, shape, size, color);
} }
} }
}
canvas.fill = function(color) { canvas.fill = function(color) {
canvas.ctx.fillStyle = color; canvas.ctx.fillStyle = color;
canvas.ctx.fillRect(0, 0, canvas.width, canvas.height); 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 index = (y * canvas.width + x) * 4;
const color = [data[index], data[index + 1], data[index + 2], data[index + 3]]; const color = [data[index], data[index + 1], data[index + 2], data[index + 3]];
return color; return color;
} }
canvas.setColorAtPixelData = function(x, y, color, data) { canvas.setRgbaArrayColorAtPixelData = function(x, y, color, data) {
const index = (y * canvas.width + x) * 4; const index = (y * canvas.width + x) * 4;
data[index] = color[0]; data[index] = color[0];
data[index + 1] = color[1]; data[index + 1] = color[1];
@ -302,12 +422,13 @@ function makeCanvas({height=600, width=800}) { // {{{
data[index + 3] = color[3]; data[index + 3] = color[3];
} }
canvas.floodFill = function(x, y, color) { canvas.floodFill = function(x, y, colorRgbaArray) {
if (!canvas.background) {
const imageData = canvas.ctx.getImageData(0, 0, canvas.width, canvas.height); const imageData = canvas.ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; const data = imageData.data;
const targetColor = canvas.getColorAtPixelData(x, y, data); const targetColor = canvas.getRgbaArrayColorAtPixelData(x, y, data);
const fillColorArray = hexToRgbArray(color); const fillColorArray = colorRgbaArray;
if (colorsMatch(targetColor, fillColorArray, tolerance)) { if (colorsMatch(targetColor, fillColorArray, tolerance)) {
return; return;
@ -317,10 +438,10 @@ function makeCanvas({height=600, width=800}) { // {{{
while (stack.length > 0) { while (stack.length > 0) {
const {x, y} = stack.pop(); const {x, y} = stack.pop();
const currentColor = canvas.getColorAtPixelData(x, y, data); const currentColor = canvas.getRgbaArrayColorAtPixelData(x, y, data);
if (colorsMatch(currentColor, targetColor, tolerance)) { if (colorsMatch(currentColor, targetColor, tolerance)) {
canvas.setColorAtPixelData(x, y, fillColorArray, data); canvas.setRgbaArrayColorAtPixelData(x, y, fillColorArray, data);
if (x > 0) stack.push({x: x - 1, y}); if (x > 0) stack.push({x: x - 1, y});
if (x < canvas.width - 1) stack.push({x: x + 1, y}); if (x < canvas.width - 1) stack.push({x: x + 1, y});
@ -331,17 +452,23 @@ function makeCanvas({height=600, width=800}) { // {{{
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; 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) { canvas.setColorAtPixel = function(x, y, color) {
const imageData = canvas.ctx.getImageData(0, 0, canvas.width, canvas.height); const imageData = canvas.ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; 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); canvas.ctx.putImageData(new ImageData(data, canvas.width, canvas.height), 0, 0);
} }
@ -364,8 +491,10 @@ function makeCanvas({height=600, width=800}) { // {{{
} }
canvas.deleteCanvas = function() { canvas.deleteCanvas = function() {
if (!background) {
canvas.remove(); canvas.remove();
} }
}
canvas.setWidth(width); canvas.setWidth(width);
canvas.setHeight(height); canvas.setHeight(height);
@ -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 = {} const layer = {}
layer.canvas = makeCanvas({height, width}); layer.canvas = makeCanvas({height, width, background});
layer.active = false; layer.active = false;
layer.opacity = 1; layer.opacity = 1;
layer.background = background;
layer.controllerElement = document.createElement('div'); layer.controllerElement = document.createElement('div');
layer.controllerElement.className = 'layer-controller'; layer.controllerElement.className = 'layer-controller';
layer.controllerElement.innerHTML = '<i class="fa-solid fa-circle-check"></i>'; layer.controllerElement.innerHTML = '<i class="fa-solid fa-circle-check"></i>';
const moveUpHandle = document.createElement('div');
moveUpHandle.classList.add('handle');
moveUpHandle.classList.add('top-right');
moveUpHandle.innerHTML = '<i class="fa-solid fa-arrow-up"></i>';
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 = '<i class="fa-solid fa-arrow-down"></i>';
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', () => { layer.controllerElement.addEventListener('click', () => {
layers.setActive(layer); layers.setActive(layer);
}); });
@ -425,7 +531,7 @@ function makeLayer({height=600, width=800}) { // {{{
return layer; return layer;
} // }}} } // }}}
function makeLayers({height=600, width=800}) { // {{{ function makeLayers({height=600, width=800, backgroundColor='rgb(255, 255, 255)'}) { // {{{
const layers = []; const layers = [];
layers.height = height; layers.height = height;
layers.width = width; layers.width = width;
@ -439,6 +545,7 @@ function makeLayers({height=600, width=800}) { // {{{
layers.setHeight = function(height) { layers.setHeight = function(height) {
layers.height = height; layers.height = height;
layers.forEach(layer => layer.canvas.setHeight(height));
easelElement.style.height = height + 2 + 'px'; easelElement.style.height = height + 2 + 'px';
} }
@ -446,11 +553,30 @@ function makeLayers({height=600, width=800}) { // {{{
layers.setWidth = function(width) { layers.setWidth = function(width) {
layers.width = width; layers.width = width;
layers.forEach(layer => layer.canvas.setWidth(width));
easelElement.style.width = width + 2 + 'px'; easelElement.style.width = width + 2 + 'px';
} }
layers.setWidth(width); 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() { layers.resetPosition = function() {
const studioRect = studioElement.getBoundingClientRect(); const studioRect = studioElement.getBoundingClientRect();
easelElement.style.left = `${studioRect.left}px`; easelElement.style.left = `${studioRect.left}px`;
@ -488,19 +614,40 @@ function makeLayers({height=600, width=800}) { // {{{
} }
layers.delete = function(layer) { layers.delete = function(layer) {
if (!layer.background) {
layer.canvas.deleteCanvas(); layer.canvas.deleteCanvas();
layers.splice(layers.indexOf(layer), 1); layers.splice(layers.indexOf(layer), 1);
layers.refresh(); layers.refresh();
} }
}
layers.deleteAll = function() { layers.deleteAll = function() {
layers.forEach(layer => layer.deleteCanvas()); layers.forEach(function(layer) {
// TODO if (!layer.background) {
layer.canvas.deleteCanvas();
layers.splice(layers.indexOf(layer), 1);
}
});
} }
layers.move = function(layer, index) { layers.moveUp = function(layer) {
layers.splice(layers.indexOf(layer), 1); if (layer.background) return;
layers.splice(index, 0, layer); 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) { layers.setActive = function(layer) {
@ -512,6 +659,9 @@ function makeLayers({height=600, width=800}) { // {{{
return layers.find(layer => layer.active); return layers.find(layer => layer.active);
} }
layers.push(makeLayer({height, width, background: true}));
layers[0].canvas.fill(backgroundColor);
return layers; return layers;
} // }}} } // }}}
@ -519,8 +669,6 @@ function makeLayers({height=600, width=800}) { // {{{
const layers = makeLayers({height: initialHeight, width: initialWidth}); const layers = makeLayers({height: initialHeight, width: initialWidth});
layers.add(); layers.add();
layers.add();
layers[0].canvas.fill('rgb(255, 255, 255)');
layers.setActive(layers[1]); layers.setActive(layers[1]);
// }}} // }}}
@ -532,10 +680,10 @@ function makeColorPreview() {
colorPreview.element = document.createElement('div'); colorPreview.element = document.createElement('div');
colorPreview.element.id = 'color-preview'; colorPreview.element.id = 'color-preview';
colorPreview.element.className = 'puck'; colorPreview.element.className = 'puck';
colorPreview.element.style.backgroundColor = color.color; colorPreview.element.style.backgroundColor = brushColor.toRgb();
commandBarElement.appendChild(colorPreview.element); commandBarElement.appendChild(colorPreview.element);
colorPreview.update = function() { colorPreview.update = function() {
colorPreview.element.style.backgroundColor = color.color; colorPreview.element.style.backgroundColor = brushColor.toRgb();
} }
return colorPreview; return colorPreview;
@ -543,6 +691,60 @@ function makeColorPreview() {
const colorPreview = 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 {{{ // COMMANDS {{{
@ -693,16 +895,28 @@ commands.add({ // change-shape {{{
func: function changeShape() { func: function changeShape() {
const currentIndex = shapes.indexOf(brushShape); const currentIndex = shapes.indexOf(brushShape);
brushShape = shapes[(currentIndex + 1) % shapes.length]; brushShape = shapes[(currentIndex + 1) % shapes.length];
brushPreview.update();
} }
}); // }}} }); // }}}
commands.add({
name: 'reset',
key: 'r',
icon: '<i class="fa-solid fa-home"></i>',
func: function resetCanvas() {
zoom = 1;
layers.zoom();
layers.resetPosition();
}
});
// }}} // }}}
// TOOLS {{{ // TOOLS {{{
// FACTORY {{{ // 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 (!name) throw new Error('No name provided');
if (!key) throw new Error('No key provided'); if (!key) throw new Error('No key provided');
if (!icon) throw new Error('No icon 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.mouseDown = mouseDown;
tool.mouseMove = mouseMove; tool.mouseMove = mouseMove;
tool.mouseUp = mouseUp; tool.mouseUp = mouseUp;
tool.mouseDrag = mouseDrag;
tool.mouseLeave = mouseLeave;
tool.active = false; tool.active = false;
tool.activate = function() { tool.activate = function() {
@ -745,8 +961,8 @@ function makeTools() {
tools.prevToolName = 'na'; tools.prevToolName = 'na';
tools.add = function({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}); const tool = makeTool({name, key, icon, mouseDown, mouseMove, mouseUp, mouseDrag, mouseLeave});
tools.push(tool); tools.push(tool);
} }
@ -788,21 +1004,28 @@ tools.add({ // brush {{{
mouseDown: function(e) { mouseDown: function(e) {
const canvas = layers.getActive().canvas; const canvas = layers.getActive().canvas;
if (brushSize === 1) { if (brushSize === 1) {
canvas.drawPixel(canvasStartX, canvasStartY, color.color); canvas.drawPixel(canvasStartX, canvasStartY, brushColor.toRgb());
} else { } else {
canvas.drawShape(canvasStartX, canvasStartY, brushShape, brushSize, color.color); canvas.drawShape(canvasStartX, canvasStartY, brushShape, brushSize, brushColor.toRgb());
} }
}, },
mouseMove: function(e) { mouseMove: function(e) {
brushPreview.show();
brushPreview.setPosition(e.clientX, e.clientY);
},
mouseDrag: function(e) {
const canvas = layers.getActive().canvas; const canvas = layers.getActive().canvas;
if (brushSize === 1) { if (brushSize === 1) {
canvas.drawLineWithPixels(canvasStartX, canvasStartY, canvasEndX, canvasEndY, color.color); canvas.drawLineWithPixels(canvasStartX, canvasStartY, canvasEndX, canvasEndY, brushColor.toRgb());
} else { } else {
canvas.drawLineWithShape(canvasStartX, canvasStartY, canvasEndX, canvasEndY, brushShape, brushSize, color.color); canvas.drawLineWithShape(canvasStartX, canvasStartY, canvasEndX, canvasEndY, brushShape, brushSize, brushColor.toRgb());
} }
canvasStartX = canvasEndX; canvasStartX = canvasEndX;
canvasStartY = canvasEndY; canvasStartY = canvasEndY;
}, },
mouseLeave: function(e) {
brushPreview.hide();
}
}); // }}} }); // }}}
tools.add({ // content-move {{{ tools.add({ // content-move {{{
@ -813,7 +1036,7 @@ tools.add({ // content-move {{{
const canvas = layers.getActive().canvas; const canvas = layers.getActive().canvas;
canvas.saveCanvas(); canvas.saveCanvas();
}, },
mouseMove: function(e) { mouseDrag: function(e) {
const canvas = layers.getActive().canvas; const canvas = layers.getActive().canvas;
canvas.clearCanvas(); canvas.clearCanvas();
canvas.restoreCanvas(dX, dY); canvas.restoreCanvas(dX, dY);
@ -828,7 +1051,7 @@ tools.add({ // move {{{
startX = e.clientX - easelElement.offsetLeft; startX = e.clientX - easelElement.offsetLeft;
startY = e.clientY - easelElement.offsetTop; startY = e.clientY - easelElement.offsetTop;
}, },
mouseMove: function(e) { mouseDrag: function(e) {
easelElement.style.left = dX + 'px'; easelElement.style.left = dX + 'px';
easelElement.style.top = dY + 'px'; easelElement.style.top = dY + 'px';
}, },
@ -838,13 +1061,11 @@ tools.add({ // zoom {{{
name: 'zoom', name: 'zoom',
key: 'z', key: 'z',
icon: '<i class="fa-solid fa-magnifying-glass"></i>', icon: '<i class="fa-solid fa-magnifying-glass"></i>',
mouseMove: function(e) { mouseDrag: function(e) {
// TODO all canvases
// const canvas = layers.getActive().canvas;
zoom += dX * dZoom; zoom += dX * dZoom;
if (zoom < 0.1) zoom = 0.1; if (zoom < 0.1) zoom = 0.1;
// canvas.style.height = canvasHeight * zoom + 'px'; layers.zoom();
// canvas.style.width = canvasWidth * zoom + 'px'; brushPreview.update();
startX = endX; startX = endX;
} }
}); // }}} }); // }}}
@ -855,7 +1076,7 @@ tools.add({ // bucket-fill {{{
icon: '<i class="fa-solid fa-fill"></i>', icon: '<i class="fa-solid fa-fill"></i>',
mouseDown: function(e) { mouseDown: function(e) {
const canvas = layers.getActive().canvas; 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 canvas = layers.getActive().canvas;
const imageData = canvas.ctx.getImageData(canvasStartX, canvasStartY, 1, 1).data; const imageData = canvas.ctx.getImageData(canvasStartX, canvasStartY, 1, 1).data;
const pickedColor = `rgb(${imageData[0]}, ${imageData[1]}, ${imageData[2]})`; const pickedColor = `rgb(${imageData[0]}, ${imageData[1]}, ${imageData[2]})`;
color.color = pickedColor; brushColor.fromRgb(pickedColor);
colorPreview.update(); colorPreview.update();
} }
}); // }}} }); // }}}
@ -877,10 +1098,18 @@ tools.add({ // brush-size {{{
key: 'd', key: 'd',
icon: '<i class="fa-regular fa-circle-dot"></i>', icon: '<i class="fa-regular fa-circle-dot"></i>',
mouseMove: function(e) { mouseMove: function(e) {
brushPreview.show();
brushPreview.setPosition(e.clientX, e.clientY);
},
mouseDrag: function(e) {
brushSize += dX * dBrushSize; brushSize += dX * dBrushSize;
if (brushSize < 1) brushSize = 1; if (brushSize < 1) brushSize = 1;
if (brushSize > maxBrushSize) brushSize = maxBrushSize; if (brushSize > maxBrushSize) brushSize = maxBrushSize;
startX = endX; startX = endX;
brushPreview.update();
},
mouseLeave: function(e) {
brushPreview.hide();
} }
}); // }}} }); // }}}
@ -888,37 +1117,39 @@ tools.add({ // resize {{{
name: 'resize', name: 'resize',
key: 'r', key: 'r',
icon: '<i class="fa-solid fa-ruler-combined"></i>', icon: '<i class="fa-solid fa-ruler-combined"></i>',
mouseMove: function(e) { mouseDrag: function(e) {
// const canvas = layers.getActive().canvas; let newWidth = layers.width + dX / zoom;
// let newWidth = canvasWidth + dX / zoom; let newHeight = layers.height + dY / zoom;
// let newHeight = canvasHeight + dY / zoom; layers.resize(newWidth, newHeight);
// if (newWidth > 0 && newHeight > 0) { startX = endX;
// canvas.setWidth(newWidth); startY = endY;
// 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);
// }
} }
}); // }}} }); // }}}
tools.add({ // color-mix {{{ tools.add({ // color-mix {{{
name: 'color-mix', name: 'color-mix',
key: 'x', key: 'x',
icon: '<i class="fa-solid fa-mortar-pestle"></i>', icon: '<i class="fa-solid fa-mortar-pestle"></i>',
mouseDown: function(e) { mouseDown: function(e) {
const startTime = Date.now(); tempColor.copy(canvasColor);
const canvas = layers.getActive().canvas; startTime = Date.now();
interval = setInterval(() => { interval = setInterval(() => {
let canvasColor = canvas.getColorAtPixel(canvasEndX, canvasEndX); if (!tempColor.match(canvasColor)) {
console.log({canvasEndX, canvasEndY, canvasColor}); startTime = Date.now();
tempColor.copy(canvasColor);
}
if (!canvasColor.isOpaque()) {
startTime = Date.now();
} else {
const elapsedTime = Date.now() - startTime; const elapsedTime = Date.now() - startTime;
const t = Math.min(1, elapsedTime / 10000); const t = Math.min(1, elapsedTime / 10000);
color.mix(canvasColor, t); brushColor.mixx(canvasColor, t);
colorPreview.update();
if (!isMouseDown) {
clearInterval(interval);
startTime = Date.now();
}
}
}, 50); }, 50);
}, },
mouseUp: function(e) { mouseUp: function(e) {
@ -927,7 +1158,6 @@ tools.add({ // color-mix {{{
mouseLeave: function(e) { mouseLeave: function(e) {
clearInterval(interval); clearInterval(interval);
} }
}); // }}} }); // }}}
// }}} // }}}
@ -963,13 +1193,13 @@ function makePuck({puckColor, key, editable=true}) {
puck.element.appendChild(keyHint); puck.element.appendChild(keyHint);
} }
puck.element.addEventListener('mousedown', (e) => { puck.element.addEventListener('mousedown', (e) => {
const startTime = Date.now(); const startTime = Date.now();
interval = setInterval(() => { interval = setInterval(() => {
const elapsedTime = Date.now() - startTime; const elapsedTime = Date.now() - startTime;
const t = Math.min(1, elapsedTime / 10000); const t = Math.min(1, elapsedTime / 10000);
color.mix(puck.element.style.backgroundColor, t); brushColor.mixxRgb(puck.element.style.backgroundColor, t);
colorPreview.update();
}, 50); }, 50);
}); });
@ -977,12 +1207,17 @@ function makePuck({puckColor, key, editable=true}) {
clearInterval(interval); clearInterval(interval);
}); });
puck.element.addEventListener('mouseleave', (e) => {
clearInterval(interval);
});
puck.keydown = function(e) { puck.keydown = function(e) {
const startTime = Date.now(); startTime = Date.now();
var interval = setInterval(() => { var interval = setInterval(() => {
const elapsedTime = Date.now() - startTime; const elapsedTime = Date.now() - startTime;
const t = Math.min(1, elapsedTime / 10000); const t = Math.min(1, elapsedTime / 10000);
color.mix(puck.element.style.backgroundColor, t); brushColor.mixxRgb(puck.element.style.backgroundColor, t);
colorPreview.update();
}, 50); }, 50);
function onKeyUp() { function onKeyUp() {
clearInterval(interval); clearInterval(interval);
@ -1012,36 +1247,53 @@ function makePucks() {
const pucks = makePucks(); const pucks = makePucks();
pucks.add({ pucks.add({ // black
puckColor: 'rgb(0, 0, 0)', puckColor: 'rgb(0, 0, 0)',
key: '1', key: '1',
editable: false, editable: false,
}); });
pucks.add({ pucks.add({ // white
puckColor: 'rgb(255, 255, 255)', puckColor: 'rgb(255, 255, 255)',
key: '2', key: '2',
editable: false, editable: false,
}); });
pucks.add({ pucks.add({ // red
puckColor: 'rgb(255, 0, 0)', puckColor: 'rgb(255, 0, 0)',
key: '3', key: '3',
editable: false, editable: false,
}); });
pucks.add({ pucks.add({ // blue
puckColor: 'rgb(0, 255, 0)', puckColor: 'rgb(0, 0, 255)',
key: '4', key: '5',
editable: false, editable: false,
}); });
pucks.add({ pucks.add({ // yellow
puckColor: 'rgb(0, 0, 255)', puckColor: 'rgb(255, 255, 0)',
key: '5', 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, editable: false,
}); });
pucks.add({ // green
puckColor: 'rgb(0, 255, 0)',
key: '4',
editable: false,
});
// }}} // }}}
@ -1134,7 +1386,7 @@ infos.add({
infos.add({ infos.add({
name: 'color', name: 'color',
updateFunction: function() { 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 {{{ // MOUSE EVENT LISTENERS {{{
@ -1179,13 +1438,13 @@ studioElement.addEventListener('mousedown', (e) => {
canvasStartY = canvas.getPositionOnCanvas(e).y; canvasStartY = canvas.getPositionOnCanvas(e).y;
canvasEndX = canvas.getPositionOnCanvas(e).x; canvasEndX = canvas.getPositionOnCanvas(e).x;
canvasEndX = canvas.getPositionOnCanvas(e).y; canvasEndX = canvas.getPositionOnCanvas(e).y;
canvasColor.fromRgbaArray(canvas.getRgbaColorArrayAtPixel(canvasStartX, canvasStartY));
for (var i = 0; i < tools.length; i++) { for (var i = 0; i < tools.length; i++) {
var tool = tools[i]; var tool = tools[i];
if (tool.active) { if (tool.active) {
if (tool.mouseDown) { if (tool.mouseDown) {
tool.mouseDown(e); tool.mouseDown(e);
break;
} }
} }
} }
@ -1204,22 +1463,23 @@ studioElement.addEventListener('mousemove', (e) => {
canvasEndY = canvas.getPositionOnCanvas(e).y; canvasEndY = canvas.getPositionOnCanvas(e).y;
canvasDX = canvasEndX - canvasStartX; canvasDX = canvasEndX - canvasStartX;
canvasDY = canvasEndY - canvasStartY; canvasDY = canvasEndY - canvasStartY;
canvasColor.fromRgbaArray(canvas.getRgbaColorArrayAtPixel(canvasEndX, canvasEndY));
if (tools.getActive().name === 'brush-size') { for (var i = 0; i < tools.length; i++) {
brushPreviewElement.style.display = 'block'; var tool = tools[i];
brushPreviewElement.style.width = brushSize + 'px'; if (tool.active) {
brushPreviewElement.style.height = brushSize + 'px'; if (tool.mouseMove) {
brushPreviewElement.style.left = e.clientX - brushSize / 2 + 'px'; tool.mouseMove(e);
brushPreviewElement.style.top = e.clientY - brushSize / 2 + 'px'; }
}
} }
if (isMouseDown) { if (isMouseDown) {
for (var i = 0; i < tools.length; i++) { for (var i = 0; i < tools.length; i++) {
var tool = tools[i]; var tool = tools[i];
if (tool.active) { if (tool.active) {
if (tool.mouseMove) { if (tool.mouseDrag) {
tool.mouseMove(e); tool.mouseDrag(e);
break;
} }
} }
} }
@ -1229,6 +1489,7 @@ studioElement.addEventListener('mousemove', (e) => {
}); });
studioElement.addEventListener('mouseup', () => { studioElement.addEventListener('mouseup', () => {
isMouseDown = false; isMouseDown = false;
@ -1258,7 +1519,6 @@ studioElement.addEventListener('mouseleave', () => {
} }
} }
brushPreviewElement.style.display = 'none';
infos.update(); infos.update();
}); });

724
temp.js

@ -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', '<i class="fa-solid fa-paintbrush"></i>', 'b');
createToolButton('content-move', 'Move Content', '<i class="fa-regular fa-hand"></i>', 'h');
createToolButton('move', 'Move Canvas', '<i class="fa-solid fa-arrows-up-down-left-right"></i>', 'm');
createToolButton('zoom', 'Zoom', '<i class="fa-solid fa-magnifying-glass"></i>', 'z');
createToolButton('resize', 'Resize', '<i class="fa-solid fa-ruler-combined"></i>', 'r');
createToolButton('color-picker', 'Color Picker', '<i class="fa-solid fa-eye-dropper"></i>', 'a');
createToolButton('color-mix', 'Color Mix', '<i class="fa-solid fa-mortar-pestle"></i>', 'x');
createToolButton('brush-size', 'Brush Size', '<i class="fa-regular fa-circle-dot"></i>', 'd');
createToolButton('bucket-fill', 'Bucket Fill', '<i class="fa-solid fa-fill"></i>', '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('<i class="fa-solid fa-download"></i>', 'Save', saveCanvas, 's'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-upload"></i>', 'Load', loadCanvas));
menuButtons.push(createMenuButton('<i class="fa-solid fa-trash-can"></i>', 'Clear', clearCanvas));
menuButtons.push(createMenuButton('<i class="fa-solid fa-floppy-disk"></i>', 'Export', exportCanvas));
menuButtons.push(createMenuButton('<i class="fa-regular fa-folder-open"></i>', 'Import', importCanvas));
menuButtons.push(createMenuButton('<i class="fa-solid fa-left-right"></i>', 'Flip Horizontally', flipCanvasHorizontally, 'f'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-up-down"></i>', 'Flip Vertically', flipCanvasVertically, 'v'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-undo"></i>', 'Undo', undo, 'u'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-redo"></i>', 'Redo', redo, 'y'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-pump-soap"></i>', 'Clear', clearCanvas, 'c'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-house"></i>', 'Reset', resetZoom, 't'));
menuButtons.push(createMenuButton('<i class="fa-solid fa-plus"></i>', '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 = '<i class="fa-solid fa-droplet"></i>';
// 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 = '<i class="fa-solid fa-fill"></i>';
// puck.appendChild(updateHandle);
// updateHandle.addEventListener('click', () => {
// puck.style.backgroundColor = color;
// });
const deleteHandle = document.createElement('div');
deleteHandle.className = 'delete-handle';
deleteHandle.innerHTML = '<i class="fa-solid fa-trash-can"></i>';
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();
// }}}
Loading…
Cancel
Save