Line Drawing Tool

Press Space to snap to right angles

目前为 2020-08-06 提交的版本。查看 最新版本

// ==UserScript==
// @name        Line Drawing Tool
// @namespace   https://greasyfork.org/users/281093
// @match       https://sketchful.io/
// @grant       none
// @version     1.3.1
// @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('keyup', (e) => {
	if (e.code === "ShiftLeft" && !canvasHidden) {
		lineCanvas.style.display = "none";
		canvasHidden = true;
		enableScroll();
		resetLineCanvas();
		document.removeEventListener('pointermove', savePos);
		document.removeEventListener('pointerup', pointerUpDraw);
	}
	else if (e.code === "Space" && snap) {
		snap = false;
		document.dispatchEvent(createMouseEvent("pointermove", realPos));
	}
});

function savePos(e) {
	previewPos = getPos(e);
	realPos = getRealPos(e);
	
	if (canvasHidden || !drawingLine) return;
	lineCanvas.clear();
	drawPreviewLine(previewPos);
	e.preventDefault();
	
}

lineCanvas.addEventListener('pointerdown', (e) => {
	origin = getPos(e);
	realOrigin = getRealPos(e);
	drawingLine = true;
	document.addEventListener('pointerup', pointerUpDraw);
	document.addEventListener('pointermove', savePos);
});


function pointerUpDraw(e) {
	document.removeEventListener('pointermove', savePos);
	document.removeEventListener('pointerup', pointerUpDraw);
	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, true));
}

function createMouseEvent(name, pos, bubbles = false) {
    return new MouseEvent(name, {
        bubbles: bubbles,
        clientX: pos.x,
        clientY: pos.y,
        button: 0
    });
}

function wrap(toWrap, wrapper) {
	toWrap.parentNode.appendChild(wrapper);
	wrapper.appendChild(toWrap);
	return wrapper;
}

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);
}