2022-11-24 09:30:13 -06:00
|
|
|
const _mask_brush_draw_callback = (evn, state) => {
|
2022-11-22 16:24:55 -06:00
|
|
|
if (evn.initialTarget.id === "overlayCanvas") {
|
|
|
|
maskPaintCtx.globalCompositeOperation = "source-over";
|
|
|
|
maskPaintCtx.strokeStyle = "#FF6A6A";
|
|
|
|
|
|
|
|
maskPaintCtx.lineWidth = state.brushSize;
|
|
|
|
maskPaintCtx.beginPath();
|
|
|
|
maskPaintCtx.moveTo(evn.px, evn.py);
|
|
|
|
maskPaintCtx.lineTo(evn.x, evn.y);
|
|
|
|
maskPaintCtx.lineJoin = maskPaintCtx.lineCap = "round";
|
|
|
|
maskPaintCtx.stroke();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-11-24 09:30:13 -06:00
|
|
|
const _mask_brush_erase_callback = (evn, state) => {
|
2022-11-22 16:24:55 -06:00
|
|
|
if (evn.initialTarget.id === "overlayCanvas") {
|
|
|
|
maskPaintCtx.globalCompositeOperation = "destination-out";
|
|
|
|
maskPaintCtx.strokeStyle = "#FFFFFFFF";
|
|
|
|
|
|
|
|
maskPaintCtx.lineWidth = state.brushSize;
|
|
|
|
maskPaintCtx.beginPath();
|
|
|
|
maskPaintCtx.moveTo(evn.px, evn.py);
|
|
|
|
maskPaintCtx.lineTo(evn.x, evn.y);
|
|
|
|
maskPaintCtx.lineJoin = maskPaintCtx.lineCap = "round";
|
|
|
|
maskPaintCtx.stroke();
|
|
|
|
}
|
|
|
|
};
|
2022-11-24 09:30:13 -06:00
|
|
|
|
|
|
|
const maskBrushTool = () =>
|
|
|
|
toolbar.registerTool(
|
|
|
|
"res/icons/paintbrush.svg",
|
|
|
|
"Mask Brush",
|
|
|
|
(state, opt) => {
|
|
|
|
// Draw new cursor immediately
|
|
|
|
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
|
|
|
state.movecb({...mouse.coords.canvas.pos, target: {id: "overlayCanvas"}});
|
|
|
|
|
|
|
|
// Start Listeners
|
|
|
|
mouse.listen.canvas.onmousemove.on(state.movecb);
|
|
|
|
mouse.listen.canvas.onwheel.on(state.wheelcb);
|
|
|
|
mouse.listen.canvas.left.onpaint.on(state.drawcb);
|
|
|
|
mouse.listen.canvas.right.onpaint.on(state.erasecb);
|
|
|
|
},
|
|
|
|
(state, opt) => {
|
|
|
|
// Clear Listeners
|
|
|
|
mouse.listen.canvas.onmousemove.clear(state.movecb);
|
2022-11-25 12:22:16 -06:00
|
|
|
mouse.listen.canvas.onwheel.clear(state.wheelcb);
|
2022-11-24 09:30:13 -06:00
|
|
|
mouse.listen.canvas.left.onpaint.clear(state.drawcb);
|
|
|
|
mouse.listen.canvas.right.onpaint.clear(state.erasecb);
|
|
|
|
},
|
|
|
|
{
|
|
|
|
init: (state) => {
|
|
|
|
state.config = {
|
|
|
|
brushScrollSpeed: 1 / 4,
|
|
|
|
minBrushSize: 10,
|
|
|
|
maxBrushSize: 500,
|
|
|
|
};
|
|
|
|
|
|
|
|
state.brushSize = 64;
|
|
|
|
state.setBrushSize = (size) => {
|
|
|
|
state.brushSize = size;
|
|
|
|
state.ctxmenu.brushSizeRange.value = size;
|
|
|
|
state.ctxmenu.brushSizeText.value = size;
|
|
|
|
};
|
|
|
|
|
|
|
|
state.movecb = (evn) => {
|
|
|
|
if (evn.target.id === "overlayCanvas") {
|
|
|
|
// draw big translucent red blob cursor
|
|
|
|
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 = "#FF6A6A50";
|
|
|
|
ovCtx.fill();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
state.wheelcb = (evn) => {
|
|
|
|
if (evn.target.id === "overlayCanvas") {
|
|
|
|
state.brushSize = state.setBrushSize(
|
|
|
|
state.brushSize -
|
|
|
|
Math.floor(state.config.brushScrollSpeed * evn.delta)
|
|
|
|
);
|
|
|
|
ovCtx.clearRect(0, 0, ovCanvas.width, ovCanvas.height);
|
|
|
|
state.movecb(evn);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
state.drawcb = (evn) => _mask_brush_draw_callback(evn, state);
|
|
|
|
state.erasecb = (evn) => _mask_brush_erase_callback(evn, state);
|
|
|
|
},
|
|
|
|
populateContextMenu: (menu, state) => {
|
|
|
|
if (!state.ctxmenu) {
|
|
|
|
state.ctxmenu = {};
|
|
|
|
const brushSizeSlider = _toolbar_input.slider(
|
|
|
|
state,
|
|
|
|
"brushSize",
|
|
|
|
"Brush Size",
|
|
|
|
state.config.minBrushSize,
|
|
|
|
state.config.maxBrushSize,
|
|
|
|
1
|
|
|
|
);
|
|
|
|
state.ctxmenu.brushSizeSlider = brushSizeSlider.slider;
|
|
|
|
state.setBrushSize = brushSizeSlider.setValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.appendChild(state.ctxmenu.brushSizeSlider);
|
|
|
|
},
|
|
|
|
shortcut: "M",
|
|
|
|
}
|
|
|
|
);
|