MutliQRCode

PC端和移动端都可用的二维码识别

目前為 2023-05-28 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         MutliQRCode
// @namespace    https://greasyfork.org/zh-CN/users/1073349
// @version      0.3
// @description  PC端和移动端都可用的二维码识别
// @author       4ehex
// @match        *://*/*
// @icon         data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgc3R5bGU9IndpZHRoOiAxZW07aGVpZ2h0OiAxZW07dmVydGljYWwtYWxpZ246IG1pZGRsZTtmaWxsOiBjdXJyZW50Q29sb3I7b3ZlcmZsb3c6IGhpZGRlbjsiIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwLWlkPSI3NjA1Ij48cGF0aCBkPSJNMTg2LjAyNjY2NyAwaDY1MS45NDY2NjZRMTAyNCAwIDEwMjQgMTg2LjAyNjY2N3Y2NTEuOTQ2NjY2UTEwMjQgMTAyNCA4MzcuOTczMzMzIDEwMjRIMTg2LjAyNjY2N1EwIDEwMjQgMCA4MzcuOTczMzMzVjE4Ni4wMjY2NjdRMCAwIDE4Ni4wMjY2NjcgMHoiIGZpbGw9IiMzNjg5RjUiIHAtaWQ9Ijc2MDYiPjwvcGF0aD48cGF0aCBkPSJNMjEzLjMzMzMzMyAzMDQuNjRBOTAuODggOTAuODggMCAwIDEgMzA0LjIxMzMzMyAyMTMuMzMzMzMzaDkwLjg4djQyLjY2NjY2N0gzMDYuMzQ2NjY3YTQ1LjY1MzMzMyA0NS42NTMzMzMgMCAwIDAtNDYuNTA2NjY3IDQ4LjY0djkxLjczMzMzM0gyMTMuMzMzMzMzek0zOTYuOCA4MTAuNjY2NjY3SDMwNi4zNDY2NjdhOTAuNDUzMzMzIDkwLjQ1MzMzMyAwIDAgMS05MC44OC05MS4zMDY2Njd2LTkxLjczMzMzM2g0Ni41MDY2NjZ2OTEuNzMzMzMzQTQ1LjY1MzMzMyA0NS42NTMzMzMgMCAwIDAgMzA4LjA1MzMzMyA3NjhoOTAuODhhMjk4LjY2NjY2NyAyOTguNjY2NjY3IDAgMCAwLTIuMTMzMzMzIDQyLjY2NjY2N3pNODEwLjY2NjY2NyA3MTkuMzZBOTAuNDUzMzMzIDkwLjQ1MzMzMyAwIDAgMSA3MTcuNjUzMzMzIDgxMC42NjY2NjdINjI3LjJ2LTQ2LjkzMzMzNGg5MC40NTMzMzNhNDUuNjUzMzMzIDQ1LjY1MzMzMyAwIDAgMCA0Ni41MDY2NjctNDYuNTA2NjY2di05MS4zMDY2NjdIODEwLjY2NjY2N3pNMjEzLjMzMzMzMyA0ODcuNjhoNTk1LjJ2NDYuOTMzMzMzSDIxMy4zMzMzMzN6TTgxMC42NjY2NjcgMzk2LjM3MzMzM2gtNDguNjRWMzA0LjY0YTQ1LjY1MzMzMyA0NS42NTMzMzMgMCAwIDAtNDYuMDgtNDYuNTA2NjY3aC05MC44OFYyMTMuMzMzMzMzaDkwLjg4YTkwLjg4IDkwLjg4IDAgMCAxIDkwLjg4IDkxLjMwNjY2N3oiIGZpbGw9IiNGRkZGRkYiIHAtaWQ9Ijc2MDciPjwvcGF0aD48L3N2Zz4=
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jsQR.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/html2canvas.min.js
// @grant        unsafeWindow
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    //全局变量 被选中的图片元素
    var g_img_ele = null;
    var g_is_moblie_env = isPhone();

    //适配移动端 很多移动端浏览器并没有实现油猴接口
    //添加'识别二维码'按钮样式
    let btn_style = document.createElement('style');
    btn_style.type = 'text/css';
    if (!g_is_moblie_env){
        btn_style.innerText = `.idtfy_div{width:5.5vw;height:2.1vh;font-size:1vh;color:#000000;background:#F8F8FF;border-radius:1.5vh;border:0.12vh solid #f2cac9;line-height:2vh;text-align:center;vertical-align:middle;z-index:99999999;display:none;position:absolute;top:20;left:20;cursor:pointer;box-shadow:0.13vh 0.13vh 0.1vh #888888;}`;
        //GM_addStyle(`.idtfy_div{width:5.5vw;height:2.1vh;font-size:1vh;color:#000000;background:#F8F8FF;border-radius:1.5vh;border:0.12vh solid #f2cac9;line-height:2vh;text-align:center;vertical-align:middle;z-index:99999999;display:none;position:absolute;top:20;left:20;cursor:pointer;box-shadow:0.13vh 0.13vh 0.1vh #888888;}`);
    }
    else{
        btn_style.innerText = `.idtfy_div{width:25vw;height:3vh;font-size:1.6vh;color:#000000;background:#F8F8FF;border-radius:1.5vh;border:0.14vw solid #f2cac9;line-height:3vh;text-align:center;vertical-align:middle;z-index:99999999;display:none;position:absolute;top:20;left:20;cursor:pointer;box-shadow:0.15vw 0.15vw 0.13vw #888888;}`;
        //GM_addStyle(`.idtfy_div{width:25vw;height:3vh;font-size:1.6vh;color:#000000;background:#F8F8FF;border-radius:1.5vh;border:0.14vw solid #f2cac9;line-height:3vh;text-align:center;vertical-align:middle;z-index:99999999;display:none;position:absolute;top:20;left:20;cursor:pointer;box-shadow:0.15vw 0.15vw 0.13vw #888888;}`);
    }
    document.head.appendChild(btn_style);

    //添加'识别二维码'按钮
    var identify_div = document.createElement('div');
    identify_div.id = 'identify_div';
    identify_div.className = 'idtfy_div';//👇添加一个图标
    identify_div.innerHTML = `<svg class="icon" style="width:1.3vh;height:1.3vh;vertical-align:middle;fill:currentColor;overflow:hidden" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3325"><path d="M613.888 715.264h-41.984C448.512 715.264 348.16 614.912 348.16 491.52s99.84-223.232 223.232-223.232h41.984c123.392 0 223.232 99.84 223.232 223.232 0.512 123.392-99.328 223.744-222.72 223.744z" fill="#CAD3FF" opacity=".2" p-id="3326"></path><path d="M873.984 940.032h-152.576c-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6h147.456v-147.456c0-14.336 11.264-25.6 25.6-25.6s25.6 11.264 25.6 25.6v152.576c0 25.088-20.48 46.08-46.08 46.08zM894.464 311.296c-14.336 0-25.6-11.264-25.6-25.6v-148.48h-147.456c-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6h152.576c25.6 0 46.08 20.48 46.08 46.08v153.6c0 13.824-11.264 25.6-25.6 25.6z m-20.48-174.08zM131.072 311.296c-14.336 0-25.6-11.264-25.6-25.6v-153.6c0-25.6 20.48-46.08 46.08-46.08h152.576c14.336 0 25.6 11.264 25.6 25.6s-11.264 25.6-25.6 25.6H156.672v148.48c0 13.824-11.776 25.6-25.6 25.6zM304.128 940.032H151.552c-25.6 0-46.08-20.48-46.08-46.08v-152.576c0-14.336 11.264-25.6 25.6-25.6s25.6 11.264 25.6 25.6v147.456h147.456c14.336 0 25.6 11.264 25.6 25.6s-11.264 25.6-25.6 25.6zM726.528 537.6H297.472c-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6h429.056c14.336 0 25.6 11.264 25.6 25.6s-11.776 25.6-25.6 25.6z" fill="#4E63DD" p-id="3327"></path></svg> 识别二维码`;
    identify_div.onclick = OnClickedIdentifyBtn;
    document.body.appendChild(identify_div);

    //添加精简后的'notie.js' 一款纯js实现的消息弹窗 github: https://github.com/jaredreich/notie
    const notie_script = document.createElement('script')
    notie_script.innerText = `var notie=function(){function D(a,b,c){document.activeElement.blur(),C++,setTimeout(function(){C--},1e3*e+10),1==C&&(z?(clearTimeout(A),clearTimeout(B),F(function(){E(a,b,c)})):E(a,b,c))}function E(b,c,d){var f;switch(z=!0,f=0,f="undefined"==typeof d?3e3:1>d?1e3:1e3*d,b){case 1:v.style.backgroundColor=g,v.onclick=function(){};break;case 2:v.style.backgroundColor=h,v.onclick=alert_outer_onclick;break;case 3:v.style.backgroundColor=i,v.onclick=alert_outer_onclick;break;case 4:v.style.backgroundColor=j,v.onclick=alert_outer_onclick}x.innerHTML=c,v.style.top="-10000px",v.style.display="table",v.style.top="-"+v.offsetHeight-5+"px",A=setTimeout(function(){a&&(v.style.boxShadow="0px 0px 10px 0px rgba(0,0,0,0.5)"),v.style.MozTransition="all "+e+"s ease",v.style.WebkitTransition="all "+e+"s ease",v.style.transition="all "+e+"s ease",v.style.top=0,B=setTimeout(function(){F(function(){})},f)},20)}function F(b){v.style.top="-"+v.offsetHeight-5+"px",setTimeout(function(){a&&(v.style.boxShadow=""),v.style.MozTransition="",v.style.WebkitTransition="",v.style.transition="",v.style.top="-10000px",z=!1,b&&b()},1e3*e+10)}var v,w,x,z,A,B,C,a=!0,b="18px",c="24px",d=600,e=.3,g="#57BF57",h="#E3B771",i="#E1715B",j="#4D82D6",k="#FFF",l="notie-alert-outer",m="notie-alert-inner",n="notie-alert-text",o=function(a){a.style.fontSize=window.innerWidth<=d?b:c},p=500,q=function(a,b,c){var d;return function(){var e=this,f=arguments,g=function(){d=null,c||a.apply(e,f)},h=c&&!d;clearTimeout(d),d=setTimeout(g,b),h&&a.apply(e,f)}};return window.addEventListener("keydown",function(a){var b=13==a.which||13==a.keyCode,c=27==a.which||27==a.keyCode;z&&(b||c)&&(clearTimeout(A),clearTimeout(B),F())}),"undefined"==typeof Element.prototype.addEventListener&&(Element.prototype.addEventListener=Window.prototype.addEventListener=function(a,b){return a="on"+a,this.attachEvent(a,b)}),v=document.createElement("div"),v.id=l,v.style.position="fixed",v.style.top="0",v.style.left="0",v.style.zIndex="999999999",v.style.height="auto",v.style.width="100%",v.style.display="none",v.style.textAlign="center",v.style.cursor="default",v.style.MozTransition="",v.style.WebkitTransition="",v.style.transition="",v.style.cursor="pointer",alert_outer_onclick=function(){clearTimeout(A),clearTimeout(B),F()},w=document.createElement("div"),w.id=m,w.style.padding="20px",w.style.display="table-cell",w.style.verticalAlign="middle",v.appendChild(w),x=document.createElement("span"),x.id=n,x.style.color=k,x.style.fontSize=window.innerWidth<=d?b:c,window.addEventListener("resize",q(o.bind(null,x),p),!0),w.appendChild(x),document.body.appendChild(v),z=!1,C=0,{alert:D,alert_hide:F}}();`;
    document.head.appendChild(notie_script);
    /*
    GM_addElement('script', {
        textContent: `var notie=function(){function D(a,b,c){document.activeElement.blur(),C++,setTimeout(function(){C--},1e3*e+10),1==C&&(z?(clearTimeout(A),clearTimeout(B),F(function(){E(a,b,c)})):E(a,b,c))}function E(b,c,d){var f;switch(z=!0,f=0,f="undefined"==typeof d?3e3:1>d?1e3:1e3*d,b){case 1:v.style.backgroundColor=g,v.onclick=function(){};break;case 2:v.style.backgroundColor=h,v.onclick=alert_outer_onclick;break;case 3:v.style.backgroundColor=i,v.onclick=alert_outer_onclick;break;case 4:v.style.backgroundColor=j,v.onclick=alert_outer_onclick}x.innerHTML=c,v.style.top="-10000px",v.style.display="table",v.style.top="-"+v.offsetHeight-5+"px",A=setTimeout(function(){a&&(v.style.boxShadow="0px 0px 10px 0px rgba(0,0,0,0.5)"),v.style.MozTransition="all "+e+"s ease",v.style.WebkitTransition="all "+e+"s ease",v.style.transition="all "+e+"s ease",v.style.top=0,B=setTimeout(function(){F(function(){})},f)},20)}function F(b){v.style.top="-"+v.offsetHeight-5+"px",setTimeout(function(){a&&(v.style.boxShadow=""),v.style.MozTransition="",v.style.WebkitTransition="",v.style.transition="",v.style.top="-10000px",z=!1,b&&b()},1e3*e+10)}var v,w,x,z,A,B,C,a=!0,b="18px",c="24px",d=600,e=.3,g="#57BF57",h="#E3B771",i="#E1715B",j="#4D82D6",k="#FFF",l="notie-alert-outer",m="notie-alert-inner",n="notie-alert-text",o=function(a){a.style.fontSize=window.innerWidth<=d?b:c},p=500,q=function(a,b,c){var d;return function(){var e=this,f=arguments,g=function(){d=null,c||a.apply(e,f)},h=c&&!d;clearTimeout(d),d=setTimeout(g,b),h&&a.apply(e,f)}};return window.addEventListener("keydown",function(a){var b=13==a.which||13==a.keyCode,c=27==a.which||27==a.keyCode;z&&(b||c)&&(clearTimeout(A),clearTimeout(B),F())}),"undefined"==typeof Element.prototype.addEventListener&&(Element.prototype.addEventListener=Window.prototype.addEventListener=function(a,b){return a="on"+a,this.attachEvent(a,b)}),v=document.createElement("div"),v.id=l,v.style.position="fixed",v.style.top="0",v.style.left="0",v.style.zIndex="999999999",v.style.height="auto",v.style.width="100%",v.style.display="none",v.style.textAlign="center",v.style.cursor="default",v.style.MozTransition="",v.style.WebkitTransition="",v.style.transition="",v.style.cursor="pointer",alert_outer_onclick=function(){clearTimeout(A),clearTimeout(B),F()},w=document.createElement("div"),w.id=m,w.style.padding="20px",w.style.display="table-cell",w.style.verticalAlign="middle",v.appendChild(w),x=document.createElement("span"),x.id=n,x.style.color=k,x.style.fontSize=window.innerWidth<=d?b:c,window.addEventListener("resize",q(o.bind(null,x),p),!0),w.appendChild(x),document.body.appendChild(v),z=!1,C=0,{alert:D,alert_hide:F}}();`
    });
    */

    if (!g_is_moblie_env){
        //电脑端 右键弹出 双击隐藏
        window.onmousedown = function(e) {
            if (e.button == 2) {//右键
                var clickedElement = e.target;
                if ((g_img_ele = GetQREle(clickedElement)) != null){
                    document.getElementById("identify_div").style.top = (e.pageY + 10) + "px";
                    document.getElementById("identify_div").style.left = (e.pageX) + "px";
                    document.getElementById("identify_div").style.display = 'block';
                }
            }
        };

        window.ondblclick = function(e) {//双击隐藏按钮
            document.getElementById("identify_div").style.display = 'none';
        }
    }
    else{
        //移动端 监听长按事件
        let time_out = 0, touch_time = 800;
        window.addEventListener("touchstart", function(e){
            time_out = setTimeout(function(){
                var touchedElement = e.target;
                if ((g_img_ele = GetQREle(touchedElement)) != null) {
                    document.getElementById("identify_div").style.top = (e.touches[0].pageY - 50) + "px";
                    document.getElementById("identify_div").style.left = (e.touches[0].pageX + 20) + "px";
                    document.getElementById("identify_div").style.display = 'block';
                }
            }, touch_time);//touch_time毫秒后弹出识别按钮

            //触碰其他地方则隐藏按钮
            if (e.target.id != 'identify_div'){
                g_img_ele = null;
                document.getElementById("identify_div").style.display = 'none';
            }
        });
        window.addEventListener("touchmove", function(e){
            // 如果触摸未达到 touch_time ms且开始移动,则清除计时器
            clearTimeout(time_out);
            time_out = 0;
        });
        window.addEventListener("touchend", function(e){
            // 如果触摸未达到 touch_time ms且离开屏幕,则清除计时器
            clearTimeout(time_out);
            time_out = 0;
        });
    }

    //通过UA判断是否是移动端
    function isPhone() {
        var info = navigator.userAgent;
        var isPhone = /mobile/i.test(info);
        return isPhone;
    }

    //从传入的元素中获取存有图片的元素 若没有则返回null
    function GetQREle(ele){
        let ret_ele = null;

        if (ele.tagName == 'IMG'){
            if (ele.src != null) ret_ele = ele;
        }
        else{
            //遍历子元素看看是否有图片
            var childs = ele.childNodes;
            if (childs.length >= 6) return ret_ele;
            for(var i = childs.length - 1; i >= 0; i--) {
                if (childs[i].tagName == 'IMG' && childs[i].src != null){
                    ret_ele = childs[i];
                    break;
                }
            }
        }

        return ret_ele;
    }

    //通过html2canvs截图并识别
    function Html2CanvasAndIndentify(ele){
        html2canvas(ele,{
            useCORS: true, //开启跨域配置
            allowTaint: false, //允许跨域图片
        }).then(function(canvas) {
            var imgData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);

            const code = jsQR(imgData.data, imgData.width, imgData.height, {
                inversionAttempts: "dontInvert",
            });
            if (code == null) {
                notie.alert(3, '识别失败!', 3);
            }else {
                let display_text = code.data, ope_html = '<div><a id="copy_btn" href="javascript:void(0);">复制</a>[placeholder_]<a style="margin-left: 20px" href="javascript:void(0);" onclick="notie.alert_hide()">关闭</a></div>';
                if (g_is_moblie_env && (code.data.length >= 40)){//如果是移动端 且识别内容过长 则隐藏一部分内容
                        display_text = code.data.substr(0, 35) + '...';
                }
                if (isValidURL(code.data)){
                    ope_html = ope_html.replace("[placeholder_]", '<a id="goto_btn" href="' + code.data + '" target="_blank" style="margin-left: 20px">转到</a>');
                }
                else{
                    ope_html = ope_html.replace("[placeholder_]", '<a id="search_btn" href="https://www.baidu.com/s?wd=' + encodeURIComponent(code.data) + '" target="_blank" style="margin-left: 20px">搜索</a>');
                }
                notie.alert(1, '<a id="all_text" style="display: none;">'+ code.data +'</a>识别到以下文本:<br><div style="word-wrap:break-word;">' + display_text + '</div>' + ope_html, 20);
                document.getElementById("copy_btn").onclick = OnClickedCopyBtn;
            }

        }).catch(function(err){
            notie.alert(3, 'html2canvas出错:<br>' + err, 5);
        });
    }


    //'识别二维码'按钮事件
    function OnClickedIdentifyBtn(){
        if ((typeof jsQR != 'function') || (typeof html2canvas != 'function')){
            notie.alert(3, '外部JS脚本未加载成功!', 3);
            return;
        }

        if (g_img_ele == null){
            notie.alert(2, '未选中任何图片', 2);
        }
        else {
            Html2CanvasAndIndentify(g_img_ele);
        }
        document.getElementById("identify_div").style.display = 'none';
    }

    //'复制'按钮单击事件
    function OnClickedCopyBtn(){
        //GM_setClipboard(document.getElementById("all_text").innerText);
        navigator.clipboard.writeText(document.getElementById("all_text").innerText)
        notie.alert_hide();
        notie.alert(1, '复制成功!', 2);
    }

    //判字符串是否为URL
    function isValidURL(str) {
        var strRegex = "^((https|http|ftp)?://)"
        + "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" //ftp的user@
        + "(([0-9]{1,3}.){3}[0-9]{1,3}" // IP形式的URL- 199.194.52.184
        + "|" // 允许IP和DOMAIN(域名)
        + "([0-9a-z_!~*'()-]+.)*" // 域名- www.
        + "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]." // 二级域名
        + "[a-z]{2,6})" // first level domain- .com or .museum
        + "(:[0-9]{1,4})?" // 端口- :80
        + "((/?)|" // a slash isn't required if there is no file name
        + "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$";
        var re=new RegExp(strRegex);
        return re.test(str);
    }
})();