WebShot

Fast shot html element

As of 2025-01-20. See the latest version.

// ==UserScript==
// @name         WebShot
// @namespace    http://tampermonkey.net/
// @version      2025-01-19
// @description  Fast shot html element
// @author       cxykevin
// @icon         https://www.google.com/s2/favicons?sz=64&domain=0.1
// @match        *://*/*
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==



(function () {
    GM_registerMenuCommand('Help', function () {
        alert("点击 F12,找到你需要的元素,然后右键添加属性输入 shot 即可截图")
        return true
    });
    GM_registerMenuCommand('Set Timeout -> 2s', function () {
        GM_setValue("global_shot_timeout", 2000)
        reg_menu()
        return true
    });
    GM_registerMenuCommand('Set Timeout -> 10s', function () {
        GM_setValue("global_shot_timeout", 10000)
        reg_menu()
        return true
    });
    var reg_1 = 0;
    var reg_2 = 0;
    function reg_menu() {
        if (reg_1 != 0) {
            GM_unregisterMenuCommand(reg_1)
        }
        if (reg_2 != 0) {
            GM_unregisterMenuCommand(reg_2)
        }
        reg_1 = GM_registerMenuCommand('Timeout = ' + GM_getValue("global_shot_timeout", 2000) + "ms", function () {
            var timeout = prompt("输入截图等待加载时间(单位ms,默认2000)")
            timeout = timeout == null || timeout == "" ? 2000 : timeout
            timeout = parseInt(timeout)
            if (isNaN(timeout)) {
                alert("请输入数字")
                return true
            }
            GM_setValue("global_shot_timeout", timeout)
            reg_menu()
            return true
        });
        reg_2 = GM_registerMenuCommand('Scale = ' + GM_getValue("scale", 1), function () {
            var scale_v = prompt("输入缩放倍数(小数)")
            scale_v = scale_v == null || scale_v == "" ? 2000 : scale_v
            scale_v = parseInt(scale_v)
            if (isNaN(scale_v)) {
                alert("请输入数字")
                return true
            }
            GM_setValue("scale", scale_v)
            reg_menu()
            return true
        });
    }
    reg_menu();
    const script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/[email protected]/dist/html-to-image.min.js'; script.type = "module";
    document.head.appendChild(script);
    async function expandImports(cssString, baseUrl) {
        const importRegex = /@import\s+url\((['"]?)([^)'"]+)\1\);?/gi;
        let imports = cssString.match(importRegex);

        if (!imports) {
            return cssString;
        }

        for (const importRule of imports) {
            const urlMatch = importRule.match(/url\((['"]?)([^)'"]+)\1\)/);
            if (urlMatch) {
                const importedUrl = urlMatch[2];
                const fullUrl = new URL(importedUrl, baseUrl).href;
                try {
                    const response = await fetch(fullUrl);
                    if (response.ok) {
                        const importedCss = await response.text();
                        const expandedCss = await expandImports(importedCss, fullUrl);
                        cssString = cssString.replace(importRule, expandedCss);
                    } else {
                        console.warn(`Failed to load imported stylesheet: ${fullUrl}`);
                    }
                } catch (error) {
                    console.error(`Error fetching imported stylesheet: ${fullUrl}`, error);
                }
            }
        }

        return cssString;
    }
    async function load_style() {
        var all_style_str = ""
        var query = document.querySelectorAll('style')
        query.forEach(element => {
            all_style_str += element.innerHTML + "\n"
        })
        const linkTags = document.querySelectorAll('link[rel="stylesheet"]');
        for (const link of linkTags) {
            const href = link.getAttribute('href');
            try {
                const response = await fetch(href);
                if (response.ok) {
                    const cssContent = await response.text();
                    all_style_str += (cssContent + "\n");
                } else {
                    console.warn(`Failed to load stylesheet: ${href}`);
                }
            } catch (error) {
                console.error(`Error fetching stylesheet: ${href}`, error);
            }
        }
        var res = extractAtRules(await expandImports(all_style_str))[0]
        return res ? res : ""
    }
    function extractAtRules(cssString) {
        const atRuleRegex = /@[\w-]+[^;{]*\{[^}]*\}/g;
        const matches = cssString.match(atRuleRegex);

        if (!matches) {
            return [];
        }

        return matches;
    }
    function shot(el) {
        if (el == undefined) {
            throw new DOMError("找不到元素id")
        }
        var el_rect = el.getBoundingClientRect();
        const boxShadowValues = window.getComputedStyle(el).boxShadow.split(" ");
        var el_bx1 = 0
        var el_bx2 = 0
        var el_bx3 = 0
        var el_bx4 = 0
        var movex_el = 0
        var movey_el = 0
        var widthx_el = 0
        var widthy_el = 0
        if (boxShadowValues.length < 9) {
            try {
                el_bx1 = parseInt(boxShadowValues[4].replace(/px/g, ""))
                el_bx2 = parseInt(boxShadowValues[5].replace(/px/g, ""))
                el_bx3 = parseInt(boxShadowValues[6].replace(/px/g, ""))
                el_bx4 = parseInt(boxShadowValues[7].replace(/px/g, ""))
                movex_el = (el_bx3 * 2 + el_bx4 - el_bx1)
                movey_el = (el_bx3 * 2 + el_bx4 - el_bx2)
                widthx_el = (el_bx3 * 2 + el_bx4 + el_bx1) + movex_el
                widthy_el = (el_bx3 * 2 + el_bx4 + el_bx2) + movey_el
            } catch (e) { }
        }
        const allElements = document.body.querySelectorAll('*');
        allElements.forEach(element => {
            if (!el.contains(element)) {
                element.style.cssText += "visibility:hidden !important;"
            }
            if (element.style.overflow != "visible" && element.style.overflow == "") {
                element.style.overflow = "hidden"
            }
        });
        el.style.transition = "none"
        el.style.animation = "none"
        el.style.visibility = "visible"
        el.style.position = "fixed"
        el.style.left = movex_el.toString() + "px"
        el.style.top = movey_el.toString() + "px"
        el.style.right = "none"
        el.style.bottom = "none"
        el.style.width = el_rect.width + "px"
        el.style.height = el_rect.height + "px"
        el.style.margin = "0px"
        var real_w = el_rect.width + widthx_el
        var real_h = el_rect.height + widthy_el
        var v_doc = document.createElement('div')
        v_doc.style.position = "fixed"
        v_doc.style.left = "0px"
        v_doc.style.top = "0px"
        v_doc.style.width = real_w + "px"
        v_doc.style.height = real_h + "px"
        v_doc.style.transformOrigin = "0px 0px"
        v_doc.style.backgroundColor = "#00000000"
        v_doc.innerHTML = document.body.innerHTML
        document.body.innerHTML = ""
        document.body.appendChild(v_doc)
        var query = document.querySelectorAll('iframe');
        query.forEach(element => {
            if (element.style.position == "static" || element.style.position == "") {
                element.style.position = "relative"
            }
        })
        load_style().then(styles => {
            var styleelem = document.createElement('style');
            styleelem.innerHTML = styles;
            v_doc.appendChild(styleelem);
            setTimeout(function () {
                v_doc.style.transform = "scale(" + GM_getValue("scale", 1) + ")"
                v_doc.style.width = real_w * GM_getValue("scale", 1) + "px"
                v_doc.style.height = real_h * GM_getValue("scale", 1) + "px"
                setTimeout(() => {
                    htmlToImage.toCanvas(v_doc)
                        .then(function (canvas) {
                            canvas.style.width = real_w + "px"
                            canvas.style.height = real_h + "px"
                            document.body.innerHTML = ""
                            document.body.appendChild(canvas);
                            canvas.toBlob((blob) => {
                                var link = document.createElement('a');
                                link.href = window.URL.createObjectURL(blob, real_w, real_h);
                                link.download = 'domshot.png';
                                link.click();
                                setTimeout(function () {
                                    location.reload()
                                }, 1000)
                            });
                        });
                }, 0)
            }, GM_getValue("global_shot_timeout", 2000))
        });
    }
    window.shot = shot
    setInterval(() => {
        var ret = document.querySelector("[shot]")
        if (ret) {
            console.log("Shot! Element:", ret)
            ret.attributes.removeNamedItem("shot")
            shot(ret)
        }
    }, 500);
})();