Draw on Page

Allows you to draw directly on webpages when you press Shift+Alt+D

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         Draw on Page
// @version      1.3
// @description  Allows you to draw directly on webpages when you press Shift+Alt+D
// @author       someRandomGuy2
// @match        *://*/*
// @grant        none
// @license      Apache-2.0
// @namespace https://greasyfork.org/users/117222
// ==/UserScript==

(function() {
    'use strict';

    let container;
    let drawingCanvas;
    let contextMenuContainer;
    let contextMenu;
    let enabled = false;
    let color = "#ff0000";

    const prerenderCanvas = document.createElement("canvas");
    const prerenderCanvasX = prerenderCanvas.getContext("2d");

    const brushes = {
        pen: function(X, startX, startY, endX, endY, pressure) {
            const movementX = endX - startX;
            const movementY = endY - startY;
            const step = 2;
            const distance = Math.sqrt(movementX * movementX + movementY * movementY);
            const size = 4 * pressure;
            const halfSize = size / 2;
            const distanceStep = Math.max(0.2, size * 0.3);
            const softness = 0.4; // = 1 - hardness
            const prerenderCanvasSize = Math.ceil(size + 12);

            if (prerenderCanvasSize == 0) { return; }

            prerenderCanvas.width = prerenderCanvasSize;
            prerenderCanvas.height = prerenderCanvasSize;
            prerenderCanvasX.fillStyle = color;
            prerenderCanvasX.beginPath();
            prerenderCanvasX.filter = `blur(0.5px)`;
            prerenderCanvasX.arc(prerenderCanvasSize / 2, prerenderCanvasSize / 2, halfSize, 0, 2 * Math.PI);
            prerenderCanvasX.fill();

            for (let i = 0; i < distance; i += distanceStep) {
                const x = startX * (1 - i / distance) + endX * (i / distance);
                const y = startY * (1 - i / distance) + endY * (i / distance);
                X.drawImage(prerenderCanvas, x - prerenderCanvasSize / 2, y - prerenderCanvasSize / 2);
            }
            X.drawImage(prerenderCanvas, endX - prerenderCanvasSize / 2, endY - prerenderCanvasSize / 2);
        },

        eraser: function(X, startX, startY, endX, endY, pressure) {
            const movementX = endX - startX;
            const movementY = endY - startY;
            const step = 2;
            const distance = Math.sqrt(movementX * movementX + movementY * movementY);
            const size = 50 * pressure;
            const halfSize = size / 2;
            const distanceStep = Math.max(0.2, size * 0.2);

            for (let i = 0; i < distance; i += distanceStep) {
                const x = startX * (1 - i / distance) + endX * (i / distance);
                const y = startY * (1 - i / distance) + endY * (i / distance);
                X.clearRect(x - halfSize, y - halfSize, size, size);
            }
            X.clearRect(endX - halfSize, endY - halfSize, size, size);
        },
    };

    function toggleDrawable() {
        initIfNotAlready();
        if (enabled) {
            enabled = false;
            container.classList.add("tm-drawing-canvas-fallthrough");
        } else {
            enabled = true;
            container.classList.remove("tm-drawing-canvas-fallthrough");
        }
    }

    function initIfNotAlready() {
        if (container) { return; }
        container = document.createElement("div");
        container.classList.add("tm-drawing-canvas-container");

        drawingCanvas = document.createElement("canvas");
        drawingCanvas.classList.add("tm-drawing-canvas");
        container.appendChild(drawingCanvas);
        drawingCanvas.width = innerWidth * devicePixelRatio;
        drawingCanvas.height = innerHeight * devicePixelRatio;

        const X = drawingCanvas.getContext("2d");

        contextMenuContainer = document.createElement("div");
        contextMenuContainer.classList.add("tm-drawing-canvas-context-menu-container");

        contextMenu = document.createElement("div");
        contextMenuContainer.appendChild(contextMenu);
        contextMenu.classList.add("tm-drawing-canvas-context-menu");

        const clearCanvasOption = document.createElement("div");
        clearCanvasOption.innerText = "Clear canvas";
        contextMenu.appendChild(clearCanvasOption);

        const saveCanvasOption = document.createElement("div");
        saveCanvasOption.innerText = "Save canvas";
        contextMenu.appendChild(saveCanvasOption);

        const changeColor = document.createElement("div");
        const colorPicker = document.createElement("input");
        colorPicker.type = "color";
        colorPicker.value = color;
        changeColor.appendChild(colorPicker);
        contextMenu.appendChild(changeColor);

        const dropShadowToggle = document.createElement("div");
        dropShadowToggle.innerText = "Toggle drop shadow";
        contextMenu.appendChild(dropShadowToggle);

        let mouseDown = false;

        drawingCanvas.addEventListener("pointerdown", function() { mouseDown = true; });
        drawingCanvas.addEventListener("pointerup", function() { mouseDown = false; });

        drawingCanvas.addEventListener("pointermove", function(event) {
            if (!mouseDown) { return; }
            const brush = event.shiftKey ? brushes.eraser : brushes.pen;
            const scaleX = drawingCanvas.width / innerWidth;
            const scaleY = drawingCanvas.height / innerHeight;
            const startX = (event.x - event.movementX) * scaleX;
            const startY = (event.y - event.movementY) * scaleY;
            const endX = event.x * scaleX;
            const endY = event.y * scaleY;

            brush(X, startX, startY, endX, endY, event.pressure);
        });

        drawingCanvas.addEventListener("contextmenu", function(event) {
            contextMenu.style.top = event.clientY + "px";
            contextMenu.style.left = event.clientX + "px";
            container.appendChild(contextMenuContainer);
            event.preventDefault();
        });

        function addContextMenuItemClickListener(elm, func) {
            elm.addEventListener("click", func);
            elm.addEventListener("mouseup", ev => func(ev, true));
        }

        function closeContextMenu() {
            container.removeChild(contextMenuContainer);
        }

        addContextMenuItemClickListener(contextMenuContainer, function(event, mouseupShortcutClick) {
            if (mouseupShortcutClick) {
                if (event.target != contextMenuContainer && event.target != contextMenu) {
                    closeContextMenu();
                }
            } else {
                closeContextMenu();
            }
        });

        addContextMenuItemClickListener(clearCanvasOption, function() {
            // clear canvas
            X.clearRect(0, 0, drawingCanvas.width, drawingCanvas.height);
            drawingCanvas.width = innerWidth * devicePixelRatio;
            drawingCanvas.height = innerHeight * devicePixelRatio;
        });

        addContextMenuItemClickListener(saveCanvasOption, function() {
            drawingCanvas.toBlob(blob => open(URL.createObjectURL(blob)))
        });

        addContextMenuItemClickListener(dropShadowToggle, function() {
            drawingCanvas.classList.toggle("no-drop-shadow");
        });

        addContextMenuItemClickListener(changeColor, function(event, mouseupShortcutClick) {
            if (event.target == changeColor || mouseupShortcutClick) {
                colorPicker.click();
            }
            event.stopPropagation();
        });

        colorPicker.addEventListener("change", function() {
            color = colorPicker.value;
            closeContextMenu();
        });

        drawingCanvas.addEventListener("touchmove", function(event) { });

        const style = document.createElement("style");
        style.innerHTML = `
        .tm-drawing-canvas-container {
          z-index: 99999;
          position: fixed;
          top: 0;
          left: 0;
          width: 100vw;
          height: 100vh;
          font-size: 14px;
          font-family: sans;
          line-height: 1.15;
          color: #000;
        }

        .tm-drawing-canvas-container.tm-drawing-canvas-fallthrough {
          pointer-events: none;
        }

        .tm-drawing-canvas {
          position: aboslute;
          top: 0;
          left: 0;
          width: 100%;
          height: 100%;
          cursor: crosshair;
          filter: drop-shadow(0px 0px 8px background);
        }

        .tm-drawing-canvas.no-drop-shadow {
          filter: none;
        }

        .tm-drawing-canvas-context-menu-container {
          position: fixed;
          top: 0;
          left: 0;
          width: 100%;
          height: 100%;
        }

        .tm-drawing-canvas-context-menu {
          position: absolute;
          display: inline-block;
          background-color: #fff;
          padding-top: 4px;
          padding-bottom: 4px;
          margin-top: 1px;
          border: 1px solid #00000040;
          box-shadow: #00000038 0px 1px 16px, #0000002e 0px 1px 8px;
        }

        .tm-drawing-canvas-context-menu div {
          padding: 4px;
          cursor: pointer;
        }

        .tm-drawing-canvas-context-menu div:hover {
          background-color: #ccc;
        }
        `;
        document.head.appendChild(style);
        document.body.appendChild(container);
    }

    addEventListener("keydown", function(event) {
        if (event.code == "KeyD" && event.altKey && event.shiftKey) {
            toggleDrawable();
        }
    });
})();