Merge pull request #65 from zero01101/bleeding-edge
fix chrome giant icon/add pipette tool (shift)
This commit is contained in:
commit
0c6ffffdc3
7 changed files with 211 additions and 9 deletions
|
@ -254,6 +254,8 @@ div.prompt-wrapper > textarea:focus {
|
|||
|
||||
/* Style Field */
|
||||
select > .style-select-option {
|
||||
position: relative;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
|
42
css/ui/tool/colorbrush.css
Normal file
42
css/ui/tool/colorbrush.css
Normal 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;
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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});
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
6
res/icons/pipette.svg
Normal 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 |
Loading…
Reference in a new issue