Merge pull request #65 from zero01101/bleeding-edge

fix chrome giant icon/add pipette tool (shift)
This commit is contained in:
Victor Seiji Hariki 2022-12-03 20:01:59 -03:00 committed by GitHub
commit 0c6ffffdc3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 211 additions and 9 deletions

View file

@ -254,6 +254,8 @@ div.prompt-wrapper > textarea:focus {
/* Style Field */
select > .style-select-option {
position: relative;
cursor: pointer;
}

View file

@ -0,0 +1,42 @@
.brush-color-picker.wrapper {
position: relative;
height: 32px;
display: flex;
flex-direction: row;
align-items: stretch;
justify-content: space-between;
}
.brush-color-picker.picker {
cursor: pointer;
flex: 1;
height: 100%;
border-bottom-left-radius: 3px;
border-top-left-radius: 3px;
padding: 0;
border: 0;
}
.brush-color-picker.eyedropper {
cursor: pointer;
border-radius: 3px;
border-bottom-left-radius: 0;
border-top-left-radius: 0;
height: 100%;
aspect-ratio: 1;
border: 0;
background-image: url("/res/icons/pipette.svg");
background-repeat: no-repeat;
background-position: center;
background-size: contain;
}

View file

@ -16,6 +16,7 @@
<!-- Tool Specific CSS -->
<link href="css/ui/tool/stamp.css" rel="stylesheet" />
<link href="css/ui/tool/colorbrush.css" rel="stylesheet" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>

View file

@ -37,6 +37,26 @@ const ovCtx = ovLayer.ctx;
const debugCanvas = debugLayer.canvas; // where mouse cursor renders
const debugCtx = debugLayer.ctx;
/**
* Function that returns a canvas with full visible information of a certain bounding box.
*
* For now, only the img is used.
*
* @param {BoundingBox} bb The bouding box to get visible data from
* @returns {HTMLCanvasElement} The canvas element containing visible image data
*/
const getVisible = (bb) => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = bb.w;
canvas.height = bb.h;
ctx.drawImage(bgLayer.canvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
ctx.drawImage(imgCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
return canvas;
};
debugLayer.hide(); // Hidden by default
layers.registerCollection("mask", {name: "Mask Layers", requiresActive: true});

View file

@ -421,7 +421,7 @@ const keyboard = {
* @returns {boolean}
*/
isPressed(code) {
return this.keys[code].pressed;
return !!this.keys[code] && this.keys[code].pressed;
},
/**
@ -431,7 +431,7 @@ const keyboard = {
* @returns {boolean}
*/
isHeld(code) {
return this.keys[code].held;
return !!this.key[code] && this.keys[code].held;
},
/**

View file

@ -37,6 +37,17 @@ const colorBrushTool = () =>
// Draw new cursor immediately
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
state.movecb({...mouse.coords.world.pos});
// Layer for eyedropper magnifiying glass
state.glassLayer = imageCollection.registerLayer(null, {
bb: {x: 0, y: 0, w: 100, h: 100},
resolution: {w: 7, h: 7},
after: maskPaintLayer,
});
state.glassLayer.canvas.style.display = "none";
state.glassLayer.canvas.style.imageRendering = "pixelated";
state.glassLayer.canvas.style.borderRadius = "50%";
state.drawLayer = imageCollection.registerLayer(null, {
after: imgLayer,
});
@ -53,6 +64,10 @@ const colorBrushTool = () =>
mouse.listen.world.onmousemove.on(state.movecb);
mouse.listen.world.onwheel.on(state.wheelcb);
keyboard.listen.onkeydown.on(state.keydowncb);
keyboard.listen.onkeyup.on(state.keyupcb);
mouse.listen.world.btn.left.onclick.on(state.leftclickcb);
mouse.listen.world.btn.left.onpaintstart.on(state.drawstartcb);
mouse.listen.world.btn.left.onpaint.on(state.drawcb);
mouse.listen.world.btn.left.onpaintend.on(state.drawendcb);
@ -69,6 +84,10 @@ const colorBrushTool = () =>
mouse.listen.world.onmousemove.clear(state.movecb);
mouse.listen.world.onwheel.clear(state.wheelcb);
keyboard.listen.onkeydown.clear(state.keydowncb);
keyboard.listen.onkeyup.clear(state.keyupcb);
mouse.listen.world.btn.left.onclick.clear(state.leftclickcb);
mouse.listen.world.btn.left.onpaintstart.clear(state.drawstartcb);
mouse.listen.world.btn.left.onpaint.clear(state.drawcb);
mouse.listen.world.btn.left.onpaintend.clear(state.drawendcb);
@ -81,6 +100,11 @@ const colorBrushTool = () =>
imageCollection.deleteLayer(state.drawLayer);
imageCollection.deleteLayer(state.eraseBackup);
imageCollection.deleteLayer(state.eraseLayer);
imageCollection.deleteLayer(state.glassLayer);
// Cancel any eyedropping
state.drawing = false;
state.disableDropper();
},
{
init: (state) => {
@ -102,13 +126,46 @@ const colorBrushTool = () =>
state.ctxmenu.brushSizeText.value = size;
};
state.eyedropper = false;
state.enableDropper = () => {
state.eyedropper = true;
state.movecb(lastMouseMoveEvn);
state.glassLayer.canvas.style.display = "block";
};
state.disableDropper = () => {
state.eyedropper = false;
state.movecb(lastMouseMoveEvn);
state.glassLayer.canvas.style.display = "none";
};
let lastMouseMoveEvn = {x: 0, y: 0};
state.movecb = (evn) => {
// draw big translucent white blob cursor
lastMouseMoveEvn = evn;
// draw drawing cursor
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
ovCtx.beginPath();
ovCtx.arc(evn.x, evn.y, state.brushSize / 2, 0, 2 * Math.PI, true); // for some reason 4x on an arc is === to 8x on a line???
ovCtx.fillStyle = state.color + "50";
ovCtx.fill();
if (state.eyedropper) {
const bb = getBoundingBox(evn.x, evn.y, 7, 7, false);
const canvas = getVisible(bb);
state.glassLayer.ctx.clearRect(0, 0, 7, 7);
state.glassLayer.ctx.drawImage(canvas, 0, 0);
state.glassLayer.moveTo(evn.x - 50, evn.y - 50);
ovCtx.beginPath();
ovCtx.arc(evn.x, evn.y, 50, 0, 2 * Math.PI, true); // for some reason 4x on an arc is === to 7x on a line???
ovCtx.strokeStyle = "black";
ovCtx.stroke();
} else {
ovCtx.beginPath();
ovCtx.arc(evn.x, evn.y, state.brushSize / 2, 0, 2 * Math.PI, true); // for some reason 4x on an arc is === to 7x on a line???
ovCtx.fillStyle = state.color + "50";
ovCtx.fill();
}
};
state.wheelcb = (evn) => {
@ -122,17 +179,68 @@ const colorBrushTool = () =>
}
};
/**
* These are basically for eyedropper purposes
*/
state.keydowncb = (evn) => {
if (lastMouseMoveEvn.target === imageCollection.inputElement)
switch (evn.code) {
case "ShiftLeft":
case "ShiftRight":
state.enableDropper();
break;
}
};
state.keyupcb = (evn) => {
switch (evn.code) {
case "ShiftLeft":
if (!keyboard.isPressed("ShiftRight")) {
state.disableDropper();
}
break;
case "ShiftRight":
if (!keyboard.isPressed("ShiftLeft")) {
state.disableDropper();
}
break;
}
};
state.leftclickcb = (evn) => {
if (evn.target === imageCollection.inputElement && state.eyedropper) {
const bb = getBoundingBox(evn.x, evn.y, 1, 1, false);
const visibleCanvas = getVisible(bb);
const dat = visibleCanvas
.getContext("2d")
.getImageData(0, 0, 1, 1).data;
state.setColor(
"#" + ((dat[0] << 16) | (dat[1] << 8) | dat[2]).toString(16)
);
}
};
/**
* Here we actually paint things
*/
state.drawstartcb = (evn) => {
if (state.eyedropper) return;
state.drawing = true;
if (state.affectMask) _mask_brush_draw_callback(evn, state);
_color_brush_draw_callback(evn, state);
};
state.drawcb = (evn) => {
if (state.eyedropper || !state.drawing) return;
if (state.affectMask) _mask_brush_draw_callback(evn, state);
_color_brush_draw_callback(evn, state);
};
state.drawendcb = (evn) => {
if (!state.drawing) return;
state.drawing = false;
const canvas = state.drawLayer.canvas;
const ctx = state.drawLayer.ctx;
@ -231,15 +339,38 @@ const colorBrushTool = () =>
state.ctxmenu.brushBlurSlider = brushBlurSlider.slider;
// Brush color
const brushColorPickerWrapper = document.createElement("div");
brushColorPickerWrapper.classList.add(
"brush-color-picker",
"wrapper"
);
const brushColorPicker = document.createElement("input");
brushColorPicker.classList.add("brush-color-picker", "picker");
brushColorPicker.type = "color";
brushColorPicker.style.width = "100%";
brushColorPicker.value = state.color;
brushColorPicker.addEventListener("input", (evn) => {
state.color = evn.target.value;
});
state.ctxmenu.brushColorPicker = brushColorPicker;
state.setColor = (color) => {
brushColorPicker.value = color;
state.color = brushColorPicker.value;
};
const brushColorEyeDropper = document.createElement("button");
brushColorEyeDropper.classList.add(
"brush-color-picker",
"eyedropper"
);
brushColorEyeDropper.addEventListener("click", () => {
state.enableDropper();
});
brushColorPickerWrapper.appendChild(brushColorPicker);
brushColorPickerWrapper.appendChild(brushColorEyeDropper);
state.ctxmenu.brushColorPicker = brushColorPickerWrapper;
}
menu.appendChild(state.ctxmenu.affectMaskCheckbox);

6
res/icons/pipette.svg Normal file
View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="m2 22 1-1h3l9-9"></path>
<path d="M3 21v-3l9-9"></path>
<path d="m15 6 3.4-3.4a2.1 2.1 0 1 1 3 3L18 9l.4.4a2.1 2.1 0 1 1-3 3l-3.8-3.8a2.1 2.1 0 1 1 3-3l.4.4Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 371 B