Browse Source

lazygit

main
Gregory Leeman 7 months ago
parent
commit
105b78d289
  1. BIN
      fontawesome/png/arrows-alt-solid.png
  2. BIN
      fontawesome/png/hand-grab-o.png
  3. BIN
      fontawesome/png/hand.png
  4. 303
      render.js
  5. 179
      style.css

BIN
fontawesome/png/arrows-alt-solid.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

BIN
fontawesome/png/hand-grab-o.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

BIN
fontawesome/png/hand.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

303
render.js

@ -9,6 +9,7 @@ const easelElement = document.getElementById('easel');
const dZoom = 0.001;
const dBrushSize = 0.5;
const dOpacity = 0.001;
const initialWidth = 800;
const initialHeight = 600;
const maxBrushSize = 500;
@ -92,7 +93,8 @@ function makeButtonElement({icon, name, func, key}) {
button.key = key;
button.icon = icon;
button.element = document.createElement('div');
button.element.className = 'button';
button.element.classList.add('button');
button.element.classList.add('bar-button');
button.element.addEventListener('click', func);
@ -393,7 +395,7 @@ function makeCanvas({height=600, width=800, background=false}) { // {{{
const dx = x2 - x1;
const dy = y2 - y1;
const distance = Math.sqrt(dx * dx + dy * dy);
const steps = Math.ceil(distance / (size / 2));
const steps = Math.ceil(distance / (size / 3));
for (let i = 0; i <= steps; i++) {
const x = Math.round(x1 + (dx * i) / steps);
@ -472,11 +474,11 @@ function makeCanvas({height=600, width=800, background=false}) { // {{{
canvas.ctx.putImageData(new ImageData(data, canvas.width, canvas.height), 0, 0);
}
canvas.toDataUrl = function() {
const dataURL = canvas.toDataURL();
const dimensions = `${canvas.width}x${canvas.height}`;
return {dataURL, dimensions};
}
// canvas.toDataUrl = function() {
// const dataURL = canvas.toDataURL();
// const dimensions = `${canvas.width}x${canvas.height}`;
// return {dataURL, dimensions};
// }
canvas.fromDataUrl = function(dataURL, dimensions) {
const img = new Image();
@ -496,6 +498,10 @@ function makeCanvas({height=600, width=800, background=false}) { // {{{
}
}
canvas.add = function(canvas2) {
canvas.ctx.drawImage(canvas2, 0, 0);
}
canvas.setWidth(width);
canvas.setHeight(height);
@ -512,11 +518,74 @@ function makeLayer({height=600, width=800, background=undefined}) { // {{{
layer.controllerElement = document.createElement('div');
layer.controllerElement.className = 'layer-controller';
layer.controllerElement.innerHTML = '<i class="fa-solid fa-circle-check"></i>';
layer.controllerElement.addEventListener('click', () => {
layer.previewElement = document.createElement('img');
layer.previewElement.className = 'layer-preview';
layer.previewElement.src = layer.canvas.toDataURL();
layer.previewElement.addEventListener('click', () => {
layers.setActive(layer);
});
layer.controllerElement.appendChild(layer.previewElement);
if (!layer.background) {
layer.moveButtons = document.createElement('div');
layer.moveButtons.classList.add('button');
layer.moveButtons.classList.add('layer-move-buttons');
layer.moveButtons.className = 'layer-move-buttons';
layer.moveUpButton = document.createElement('div');
layer.moveUpButton.classList.add('button');
layer.moveUpButton.classList.add('layer-move-button');
layer.moveUpButton.innerHTML = '<i class="fa-solid fa-arrow-up"></i>';
layer.moveUpButton.addEventListener('click', () => {
layers.moveUp(layer);
});
layer.moveButtons.appendChild(layer.moveUpButton);
layer.moveDownButton = document.createElement('div');
layer.moveDownButton.classList.add('button');
layer.moveDownButton.classList.add('layer-move-button');
layer.moveDownButton.innerHTML = '<i class="fa-solid fa-arrow-down"></i>';
layer.moveDownButton.addEventListener('click', () => {
layers.moveDown(layer);
});
layer.moveButtons.appendChild(layer.moveDownButton);
layer.controllerElement.appendChild(layer.moveButtons);
layer.mergeButtons = document.createElement('div');
layer.mergeButtons.classList.add('button');
layer.mergeButtons.classList.add('layer-merge-buttons');
layer.mergeButtons.className = 'layer-merge-buttons';
layer.mergeUpButton = document.createElement('div');
layer.mergeUpButton.classList.add('button');
layer.mergeUpButton.classList.add('layer-merge-button');
layer.mergeUpButton.innerHTML = '<i class="fa-solid fa-angles-up"></i>';
layer.mergeUpButton.addEventListener('click', () => {
layers.mergeUp(layer);
});
layer.mergeButtons.appendChild(layer.mergeUpButton);
layer.controllerElement.appendChild(layer.mergeButtons);
layer.deleteButton = document.createElement('div');
layer.deleteButton.classList.add('button');
layer.deleteButton.classList.add('layer-delete-button');
layer.deleteButton.innerHTML = '<i class="fa-solid fa-trash-can"></i>';
layer.deleteButton.addEventListener('click', () => {
layers.delete(layer);
});
layer.controllerElement.appendChild(layer.deleteButton);
}
layer.activate = function() {
layer.active = true;
@ -528,6 +597,16 @@ function makeLayer({height=600, width=800, background=undefined}) { // {{{
layer.controllerElement.classList.remove('active');
}
layer.refreshPreview = function() {
layer.previewElement.src = layer.canvas.toDataURL();
}
layer.changeOpacity = function(opacity) {
console.log({opacity});
layer.opacity = opacity;
layer.canvas.style.opacity = opacity
}
return layer;
} // }}}
@ -537,7 +616,8 @@ function makeLayers({height=600, width=800, backgroundColor='rgb(255, 255, 255)'
layers.width = width;
layers.addButton = document.createElement('div');
layers.addButton.className = 'layer-add-button';
layers.addButton.classList.add('button');
layers.addButton.classList.add('layer-add-button');
layers.addButton.innerHTML = '<i class="fa-solid fa-plus"></i>';
layers.addButton.addEventListener('click', () => {
layers.add();
@ -598,9 +678,16 @@ function makeLayers({height=600, width=800, backgroundColor='rgb(255, 255, 255)'
});
}
layers.refreshPreviews = function() {
layers.forEach(layer => {
layer.refreshPreview();
});
}
layers.refresh = function() {
layers.refreshControllers();
layers.refreshLayers();
layers.refreshPreviews();
}
layers.add = function() {
@ -609,7 +696,7 @@ function makeLayers({height=600, width=800, backgroundColor='rgb(255, 255, 255)'
width: layers.width,
});
layers.push(layer);
layer.activate();
layers.setActive(layer);
layers.refresh();
}
@ -630,7 +717,7 @@ function makeLayers({height=600, width=800, backgroundColor='rgb(255, 255, 255)'
});
}
layers.moveUp = function(layer) {
layers.moveDown = function(layer) {
if (layer.background) return;
if (layers.indexOf(layer) === layers.length - 1) return;
const index = layers.indexOf(layer);
@ -640,7 +727,7 @@ function makeLayers({height=600, width=800, backgroundColor='rgb(255, 255, 255)'
layers.refresh();
}
layers.moveDown = function(layer) {
layers.moveUp = function(layer) {
if (layer.background) return;
if (layers.indexOf(layer) === 1) return;
const index = layers.indexOf(layer);
@ -650,6 +737,51 @@ function makeLayers({height=600, width=800, backgroundColor='rgb(255, 255, 255)'
layers.refresh();
}
layers.mergeUp = function(layer) {
if (layer.background) return;
const index = layers.indexOf(layer);
const belowLayer = layers[index - 1];
if (belowLayer.background) return;
belowLayer.canvas.add(layer.canvas);
layers.delete(layer);
layers.setActive(belowLayer);
}
layers.mergeDown = function(layer) {
if (layer.background) return;
const index = layers.indexOf(layer);
if (index === layers.length - 1) return;
const aboveLayer = layers[index + 1];
aboveLayer.canvas.add(layer.canvas);
layers.delete(layer);
layers.setActive(aboveLayer);
}
layers.mergeAll = function() {
const backgroundLayer = layers[0];
layers.forEach(layer => {
if (layer !== backgroundLayer) {
backgroundLayer.canvas.add(layer.canvas);
}
});
layers.deleteAll();
}
layers.tempMergeAll = function() {
const backgroundLayerCopy = makeLayer({
height: layers.height,
width: layers.width,
background: true,
});
// backgroundLayerCopy.canvas.fill(backgroundColor);
layers.forEach(layer => {
if (!layer.background) {
backgroundLayerCopy.canvas.add(layer.canvas);
}
});
return backgroundLayerCopy;
}
layers.setActive = function(layer) {
layers.forEach(layer => layer.deactivate());
layer.activate();
@ -838,10 +970,10 @@ commands.add({ // export {{{
key: 'e',
icon: '<i class="fa-solid fa-floppy-disk"></i>',
func: function exportCanvas() {
const canvas = layers.getActive().canvas;
const mergedCanvas = layers.tempMergeAll().canvas;
const link = document.createElement('a');
link.download = 'canvas.png';
link.href = canvas.toDataURL();
link.href = mergedCanvas.toDataURL();
link.click();
}
}); // }}}
@ -851,22 +983,24 @@ commands.add({ // import {{{
key: 'i',
icon: '<i class="fa-regular fa-folder-open"></i>',
func: function importCanvas() {
const canvas = layers.getActive().canvas;
const ctx = canvas.ctx;
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.onchange = (e) => {
input.onchange = function(e) {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = (e) => {
reader.onload = function(e) {
const dataURL = e.target.result;
const img = new Image();
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
img.src = dataURL;
img.onload = function() {
layers.add();
const canvas = layers.getActive().canvas;
canvas.fromDataUrl(dataURL, `${img.width}x${img.height}`);
layers.setWidth(img.width);
layers.setHeight(img.height);
layers.refresh();
}
img.src = e.target.result;
}
reader.readAsDataURL(file);
}
@ -899,7 +1033,7 @@ commands.add({ // change-shape {{{
}
}); // }}}
commands.add({
commands.add({ //reset {{{
name: 'reset',
key: 'r',
icon: '<i class="fa-solid fa-home"></i>',
@ -912,6 +1046,8 @@ commands.add({
// }}}
// }}}
// TOOLS {{{
// FACTORY {{{
@ -962,7 +1098,7 @@ function makeTools() {
tools.prevToolName = 'na';
tools.add = function({name, key, icon, mouseDown, mouseMove, mouseUp, mouseDrag, mouseLeave}) {
const tool = makeTool({name, key, icon, mouseDown, mouseMove, mouseUp, mouseDrag, mouseLeave});
const tool = makeTool({name, key, icon, mouseDown, mouseMove, mouseUp, mouseDrag, mouseLeave});
tools.push(tool);
}
@ -1023,6 +1159,9 @@ tools.add({ // brush {{{
canvasStartX = canvasEndX;
canvasStartY = canvasEndY;
},
mouseUp: function(e) {
layers.getActive().refreshPreview();
},
mouseLeave: function(e) {
brushPreview.hide();
}
@ -1041,6 +1180,9 @@ tools.add({ // content-move {{{
canvas.clearCanvas();
canvas.restoreCanvas(dX, dY);
},
mouseUp: function(e) {
layers.getActive().refreshPreview();
},
}); // }}}
tools.add({ // move {{{
@ -1055,6 +1197,7 @@ tools.add({ // move {{{
easelElement.style.left = dX + 'px';
easelElement.style.top = dY + 'px';
},
cursor: 'fontawesome/png/arrows-alt-solid.png',
}); // }}}
tools.add({ // zoom {{{
@ -1077,6 +1220,9 @@ tools.add({ // bucket-fill {{{
mouseDown: function(e) {
const canvas = layers.getActive().canvas;
canvas.floodFill(canvasStartX, canvasStartY, brushColor.toRgbaArray());
},
mouseUp: function(e) {
layers.getActive().refreshPreview();
}
}); // }}}
@ -1123,6 +1269,9 @@ tools.add({ // resize {{{
layers.resize(newWidth, newHeight);
startX = endX;
startY = endY;
},
mouseUp: function(e) {
layers.refreshPreviews();
}
}); // }}}
@ -1160,13 +1309,27 @@ tools.add({ // color-mix {{{
}
}); // }}}
// tools.add({ // opacity {{{
// name: 'opacity',
// key: 'o',
// icon: '<i class="fa-solid fa-ghost"></i>',
// mouseDrag: function(e) {
// layer = layers.getActive();
// var opacity = layer.opacity += dX * dOpacity;
// if (opacity < 0) opacity = 0;
// if (opacity > 1) opacity = 1;
// layer.changeOpacity(opacity);
// startX = endX;
// },
// }); // }}}
// }}}
// PUCKS {{{
// FACTORY {{{
function makePuck({puckColor, key, editable=true}) {
function makePuck({puckColor, key}) {
if (!puckColor) throw new Error('No puck color provided');
@ -1175,16 +1338,6 @@ function makePuck({puckColor, key, editable=true}) {
puck.element.style.backgroundColor = puckColor;
puck.element.className = 'puck';
if (editable) {
const deleteHandle = document.createElement('div');
deleteHandle.className = 'delete-handle';
deleteHandle.innerHTML = '<i class="fa-solid fa-trash-can"></i>';
puck.element.appendChild(deleteHandle);
deleteHandle.addEventListener('click', () => {
puck.element.remove();
});
}
if (key) {
puck.key = key;
const keyHint = document.createElement('div');
@ -1234,8 +1387,8 @@ function makePuck({puckColor, key, editable=true}) {
function makePucks() {
const pucks = [];
pucks.add = function({puckColor, key, editable}) {
const puck = makePuck({puckColor, key, editable});
pucks.add = function({puckColor, key}) {
const puck = makePuck({puckColor, key});
pucks.push(puck);
}
@ -1249,50 +1402,68 @@ const pucks = makePucks();
pucks.add({ // black
puckColor: 'rgb(0, 0, 0)',
key: '1',
editable: false,
});
pucks.add({ // white
puckColor: 'rgb(255, 255, 255)',
key: '2',
editable: false,
});
pucks.add({ // Cadmium Yellow
puckColor: 'rgb(254, 236, 0)',
});
pucks.add({ // Hansa Yellow
puckColor: 'rgb(252, 211, 0)',
});
pucks.add({ // Cadmium Orange
puckColor: 'rgb(255, 105, 0)',
});
pucks.add({ // Cadmium Red
puckColor: 'rgb(255, 39, 2)',
});
pucks.add({ // Quinacridone Magenta
puckColor: 'rgb(128, 2, 46)',
});
pucks.add({ // Cobalt Violet
puckColor: 'rgb(78, 0, 66)',
});
pucks.add({ // Ultramarine Blue
puckColor: 'rgb(25, 0, 89)',
});
pucks.add({ // Cobalt Blue
puckColor: 'rgb(0, 33, 133)',
});
pucks.add({ // Phthalo Blue
puckColor: 'rgb(13, 27, 68)',
});
pucks.add({ // Phthalo Green
puckColor: 'rgb(0, 60, 50)',
});
pucks.add({ // Permanent Green
puckColor: 'rgb(7, 109, 22)',
});
pucks.add({ // Sap Green
puckColor: 'rgb(107, 148, 4)',
});
pucks.add({ // Burnt Sienna
puckColor: 'rgb(123, 72, 0)',
});
pucks.add({ // red
puckColor: 'rgb(255, 0, 0)',
key: '3',
editable: false,
});
pucks.add({ // green
puckColor: 'rgb(0, 255, 0)',
});
pucks.add({ // blue
puckColor: 'rgb(0, 0, 255)',
key: '5',
editable: false,
});
pucks.add({ // yellow
puckColor: 'rgb(255, 255, 0)',
key: '6',
editable: false,
});
pucks.add({ // cyan
puckColor: 'rgb(0, 255, 255)',
key: '7',
editable: false,
});
pucks.add({ // yellow
puckColor: 'rgb(255, 255, 0)',
});
pucks.add({ // magenta
puckColor: 'rgb(255, 0, 255)',
key: '8',
editable: false,
});
pucks.add({ // green
puckColor: 'rgb(0, 255, 0)',
key: '4',
editable: false,
});
// }}}

179
style.css

@ -1,3 +1,13 @@
:root {
--white: #FFFFFF;
--button: #DDE4E7;
--hover: #B3BBBD;
--active: #8F9598;
/* --background: #7E888B; */
--background: #949C9E;
--black: #000000;
}
@font-face {
font-family: 'VT323';
src: url('fonts/VT323-Regular.ttf') format('truetype');
@ -15,7 +25,8 @@ body {
height: 100vh;
width: 100vw;
font-family: 'VT323', monospace;
background-color: darkgray;
background-color: var(--background);
color: var(--black);
}
#menu-bar,
@ -31,7 +42,7 @@ body {
#menu-bar, #info-bar {
padding: 10px;
background-color: #ddd;
background-color: var(--button);
display: flex;
flex-direction: row;
gap: 10px;
@ -59,7 +70,7 @@ body {
padding: 10px;
padding-top: 0;
padding-bottom: 0;
background-color: #ddd;
background-color: var(--button);
gap: 10px;
height: 100%;
flex-direction: column;
@ -79,7 +90,6 @@ body {
z-index: -1;
}
canvas {
position: absolute;
top: 0;
@ -87,19 +97,30 @@ canvas {
}
.button {
position: relative;
flex-shrink: 0;
background-color: #ddd;
background-color: var(--button);
border: 1px solid;
border-radius: 2px;
cursor: pointer;
display: flex;
text-align: center;
justify-content: center;
}
.button:hover {
background-color: var(--hover);
}
.button.active, .button:active {
background-color: var(--active);
}
.bar-button {
position: relative;
flex-shrink: 0;
width: 30px;
height: 30px;
padding: 5px;
display: flex;
flex-direction: column;
text-align: center;
justify-content: center;
cursor: pointer;
}
.puck {
@ -111,136 +132,72 @@ canvas {
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;
}
.delete-handle {
position: absolute;
top: 0;
right: 0;
font-size: .6em;
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;
}
.key-hint {
display: block;
height: 13px;
width: 9px;
line-height: 8px;
/* width: 12px; */
position: absolute;
top: 2px;
left: 1px;
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; */
text-shadow:
1px 1px 0 #000,
-1px 1px 0 #000,
-1px -1px 0 #000,
1px -1px 0 #000;
color: white;
color: var(--white);
pointer-events: none;
}
.button.active, .button:active {
background-color: darkgray;
}
#layer-controllers {
background-color: darkgrey;
background-color: var(--background);
border: 1px solid;
height: 100%;
}
.layer-controller {
background-color: lightgray;
height: 30px;
width: 60px;
background-color: var(--button);
height: 40px;
width: 90px;
border: 1px solid;
border-radius: 2px;
padding: 5px;
padding: 2px;
position: relative;
margin: 1px;
display: flex;
flex-direction: row;
gap: 2px;
align-items: center;
justify-content: space-between;
}
.layer-controller > i {
color: darkgray;
cursor: pointer;
.layer-controller.active {
background-color: var(--active);
}
.layer-controller.active > i {
color: black;
}
.handle {
display: block;
font-size: 9px;
height: 11px;
width: 24px;
position: absolute;
justify-content: center;
text-align: center;
cursor: pointer;
border-radius: 2px;
border: 1px solid;
margin: 2px;
.layer-controller:hover {
background-color: var(--hover);
}
.layer-add-button {
background-color: lightgray;
background-color: var(--button);
height: 12px;
width: 60px;
width: 100%;
border: 1px solid;
border-radius: 2px;
position: relative;
margin: 1px;
text-align: center;
font-size: 10px;
font-size: .7em;
cursor: pointer;
}
.layer-add-button:hover {
background-color: darkgray;
}
.handle:hover {
background-color: darkgray;
background-color: var(--hover);
}
.top-left {
@ -262,3 +219,37 @@ canvas {
bottom: 0;
left: 0;
}
.layer-preview {
width: 30px;
height: 30px;
border: 1px solid;
object-fit: contain;
background-color: var(--background);
cursor: pointer;
}
.layer-move-buttons, .layer-merge-buttons {
font-size: .7em;
display: flex;
flex-direction: column;
gap: 2px;
flex-grow: 1;
height: 100%;
}
.layer-delete-button {
font-size: .7em;
flex-grow: 1;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
.layer-move-button, .layer-merge-button {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
}

Loading…
Cancel
Save