// ==UserScript==
// @name Line Drawing Tool
// @namespace https://greasyfork.org/users/281093
// @match https://sketchful.io/
// @grant none
// @version 1.2
// @license MIT
// @author Bell
// @description Press Space to snap to right angles
// ==/UserScript==
/* jshint esversion: 6 */
/* eslint-disable no-undef */
const canvas = document.querySelector("#canvas");
const ctx = canvas.getContext("2d");
const lineCanvas = document.createElement("canvas");
const lineCtx = lineCanvas.getContext("2d");
lineCanvas.style.position = "absolute";
lineCanvas.style.cursor = "crosshair";
lineCanvas.style.width = "100%";
lineCanvas.style.display = "none";
lineCanvas.style.userSelect = "none";
lineCanvas.oncontextmenu = () => { return false; };
[lineCanvas.width, lineCanvas.height] = [canvas.width, canvas.height];
canvas.parentElement.insertBefore(lineCanvas, canvas);
lineCanvas.clear = () => {
lineCtx.clearRect(0, 0, lineCanvas.width, lineCanvas.height);
};
let origin = {};
let realOrigin = {};
let previewPos = {};
let realPos = {};
let canvasHidden = true;
let drawingLine = false;
let snap = false;
document.addEventListener('keydown', (e) => {
if (e.code === "ShiftLeft" && canvasHidden) {
lineCanvas.style.display = "";
disableScroll();
canvasHidden = false;
}
else if (e.code === "Space" && !snap && !canvasHidden) {
snap = true;
document.dispatchEvent(createMouseEvent("pointermove", realPos));
}
});
document.addEventListener('pointermove', (e) => {
previewPos = getPos(e);
realPos = getRealPos(e);
if (canvasHidden || !drawingLine) return;
lineCanvas.clear();
drawPreviewLine(previewPos);
e.preventDefault();
});
document.addEventListener('keyup', (e) => {
if (e.code === "ShiftLeft" && !canvasHidden) {
lineCanvas.style.display = "none";
canvasHidden = true;
enableScroll();
resetLineCanvas();
}
else if (e.code === "Space" && snap) {
snap = false;
document.dispatchEvent(createMouseEvent("pointermove", realPos));
}
});
lineCanvas.addEventListener('pointerdown', (e) => {
origin = getPos(e);
realOrigin = getRealPos(e);
drawingLine = true;
});
document.addEventListener('pointerup', (e) => {
if (!drawingLine) return;
lineCanvas.style.pointerEvents = "all";
drawLine(realOrigin.x, realOrigin.y, realPos.x, realPos.y);
resetLineCanvas();
});
function resetLineCanvas() {
drawingLine = false;
lineCanvas.clear();
}
function getPos(event) {
const canvasRect = canvas.getBoundingClientRect();
const canvasScale = canvas.width / canvasRect.width;
return {
x: (event.clientX - canvasRect.left) * canvasScale,
y: (event.clientY - canvasRect.top) * canvasScale,
};
}
function getRealPos(event) {
return {
x: event.clientX,
y: event.clientY
};
}
function drawPreviewLine(pos) {
lineCtx.beginPath();
lineCtx.moveTo(origin.x, origin.y);
if (snap) {
if (Math.abs(pos.x - origin.x) < Math.abs(pos.y - origin.y)) {
lineCtx.lineTo(origin.x, pos.y);
} else {
lineCtx.lineTo(pos.x, origin.y);
}
} else {
lineCtx.lineTo(pos.x, pos.y);
}
lineCtx.stroke();
}
function drawLine(x1, y1, x2, y2) {
let coords = { x: x1, y: y1 };
let newCoords = { x: x2, y: y2 };
if (snap) {
if (Math.abs(x2 - x1) < Math.abs(y2 - y1)) {
newCoords.x = x1;
} else {
newCoords.y = y1;
}
}
canvas.dispatchEvent(createMouseEvent("pointerdown", coords));
canvas.dispatchEvent(createMouseEvent("pointermove", newCoords));
canvas.dispatchEvent(createMouseEvent("pointerup", newCoords));
}
function createMouseEvent(name, pos) {
return new MouseEvent(name, {
bubbles: false,
clientX: pos.x,
clientY: pos.y,
button: 0
});
}
const keys = {32: 1, 37: 1, 38: 1, 39: 1, 40: 1};
function preventDefault(e) {
e.preventDefault();
}
function preventDefaultForScrollKeys(e) {
if (keys[e.keyCode]) {
preventDefault(e);
return false;
}
}
// modern Chrome requires { passive: false } when adding event
let supportsPassive = false;
try {
window.addEventListener("test", null, Object.defineProperty({}, 'passive', {
get: function () { supportsPassive = true; }
}));
} catch(e) {}
const wheelOpt = supportsPassive ? { passive: false } : false;
const wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel' : 'mousewheel';
function disableScroll() {
window.addEventListener('DOMMouseScroll', preventDefault, false); // older FF
window.addEventListener(wheelEvent, preventDefault, wheelOpt); // modern desktop
window.addEventListener('touchmove', preventDefault, wheelOpt);
window.addEventListener('keydown', preventDefaultForScrollKeys, false);
}
function enableScroll() {
window.removeEventListener('DOMMouseScroll', preventDefault, false);
window.removeEventListener(wheelEvent, preventDefault, wheelOpt);
window.removeEventListener('touchmove', preventDefault, wheelOpt);
window.removeEventListener('keydown', preventDefaultForScrollKeys, false);
}