// ==UserScript== // @name 手机浏览器触摸手势 // @name:en Mobile browser touch gestures // @description 为手机浏览器添加触摸手势功能,例如↓↑回到顶部,↑↓回到底部,→←后退,←→前进,→↓关闭标签页,→↑恢复刚关闭的页面等。还有特殊的文字手势、图片手势和视频手势,还可自定义你的手势功能。推荐使用Kiwi浏览器、Yandex浏览器和狐猴浏览器。 // @description:en Add touch gestures to mobile browsers. For example, ↓↑: go to the top, ↑↓: go to the bottom, →←: go back, ←→: go forward, →↓: closes the tab, →↑: restores just closed page, etc. There are also special text gestures, picture gestures and video gestures, and you can customize your gesture functions. Recommend using Kiwi browser, Yandex browser and Lemur Browser. // @version 8.6.1 // @author L.Xavier // @namespace https://greasyfork.org/zh-CN/users/128493 // @match *://*/* // @license MIT // @grant GM_setValue // @grant GM_getValue // @grant window.close // @grant GM_openInTab // @grant GM_setClipboard // @grant GM_addValueChangeListener // @run-at document-body // @downloadURL none // ==/UserScript== // v8.6.1 2022-09-19 - 调整滑动误差判定,精简部分代码 (()=>{ /*手势数据模块*/ let gesture={ '↑→↓←':'打开设置', '◆◆':'视频全屏', '●':'手势穿透', '→←':'后退', '←→':'前进', '↓↑':'回到顶部', '↑↓':'回到底部', '←↓':'刷新页面', '←↑':'新建页面', '→↓':'关闭页面', '→↑':'恢复页面', '↓↑●':'新页面打开', '↑↓●':'隐藏元素', '↑→':'复制页面', '→←→':'半屏模式', '→↓↑←':'视频解析', 'T→↑':'百度翻译', 'T←↑':'有道翻译', 'T◆◆':'双击搜索', 'I↓↑●':'打开图片', 'I→↑●':'百度搜图', 'V→':'前进10s', 'V←':'后退10s', 'V↑':'增加倍速', 'V↓':'减小倍速', 'V→●':'快进播放', 'V→○':'停止快进', 'V←●':'快退播放', 'V←○':'停止快退', 'V↑●':'增加音量', 'V↑○':'关闭增加音量', 'V↓●':'减少音量', 'V↓○':'关闭减少音量', 'V→▼':'右滑进度', 'V→▽':'关闭右滑进度', 'V←▼':'左滑进度', 'V←▽':'关闭左滑进度' }, pathFn={ '打开设置':'/*ONLY TOP*/openSet();', '视频全屏':'videoFullScreen();', '手势穿透':'setTimeout(()=>{if(/^[TIV]/.test(path)){path="";if(path.indexOf("I")>-1){path="I";}return;}if(gestureData.touchEle.nodeName!="IMG"){let imgs=gestureData.touchEle.parentNode.getElementsByTagName("img");for(let Ti=0,len=imgs.length;TiimgRect.x && gestureData.touchStart.clientX<(imgRect.x+imgRect.width) && gestureData.touchStart.clientY>imgRect.y && gestureData.touchStart.clientY<(imgRect.y+imgRect.height)){gestureData.touchEle=imgs[Ti];break;}}}if(gestureData.touchEle.nodeName=="IMG" && settings["图片手势"]){path="I";}else if(gestureData.touchEle.style.backgroundImage && settings["图片手势"]){gestureData.touchEle.src=gestureData.touchEle.style.backgroundImage.split(\'"\')[1];path="I";}});', '后退':'/*ONLY TOP*/history.go(-1);gestureData.backTimer=setTimeout(()=>{window.close();},500);', '前进':'/*ONLY TOP*/history.go(1);', '回到顶部':'/*WITH TOP*/let boxNode=gestureData.touchEle.parentNode;while(boxNode.nodeName!="#document"){boxNode.scrollTop=0;boxNode=boxNode.parentNode;}', '回到底部':'/*WITH TOP*/let boxNode=gestureData.touchEle.parentNode;while(boxNode.nodeName!="#document"){if(getComputedStyle(boxNode).overflowY!="hidden"){boxNode.scrollTop=boxNode.scrollHeight;}boxNode=boxNode.parentNode;}', '刷新页面':'/*ONLY TOP*/document.documentElement.style.cssText="filter:grayscale(100%)";history.go(0);', '新建页面':'/*ONLY TOP*/GM_openInTab("https://limestart.cn");', '关闭页面':'/*ONLY TOP*/window.close();', '恢复页面':'/*ONLY TOP*/GM_openInTab("chrome-native://recent-tabs");', '新页面打开':'let linkNode=gestureData.touchEle;while(true){if(linkNode.href){GM_openInTab(linkNode.href);break;}linkNode=linkNode.parentNode;if(linkNode.nodeName=="BODY"){gestureData.touchEle.click();break;}}', '隐藏元素':'let boxNode=gestureData.touchEle,area=boxNode.offsetWidth*boxNode.offsetHeight;if((area/document.body.offsetWidth/document.body.offsetHeight)<0.9){while(boxNode.nodeName!="BODY" && (area/boxNode.parentNode.offsetWidth/boxNode.parentNode.offsetHeight)>0.25){boxNode=boxNode.parentNode;}}if(boxNode.nodeName!="HTML"){boxNode.remove();}', '复制页面':'/*ONLY TOP*/GM_openInTab(location.href);', '半屏模式':'/*ONLY TOP*/if(gestureData.halfScreen){setTimeout(()=>{gestureData.halfScreen.remove();halfClose.remove();gestureData.halfScreen=null;document.documentElement.scrollTop=gestureData.scrollTop;},499);gestureData.scrollTop=document.body.scrollTop;let halfClose=addStyle("html{transform:translateY(0);}");}else{gestureData.scrollTop=document.documentElement.scrollTop;gestureData.halfScreen=addStyle("html,body{height:43vh !important;overflow-x:hidden !important;}html{transform:translateY(50vh);transition:0.5s;}");document.body.scrollTop=gestureData.scrollTop;}', '视频解析':'/*ONLY TOP*/GM_openInTab("https://jx.bozrc.com:4433/player/?url="+location.href);', '百度翻译':'GM_openInTab("https://fanyi.baidu.com/#auto/auto/"+gestureData.selectWords);', '有道翻译':'GM_openInTab("https://dict.youdao.com/w/eng/"+gestureData.selectWords);', '双击搜索':'GM_setClipboard(gestureData.selectWords);let regURL=/^(https?:\\/\\/)?([\\w\\-]+\\.)+\\w{2,4}(\\/\\S*)?$/;if(!regURL.test(gestureData.selectWords.replace(/\\s+$/,""))){gestureData.selectWords="https://bing.com/search?q="+encodeURIComponent(gestureData.selectWords);}else if(!/^http/.test(gestureData.selectWords)){gestureData.selectWords="//"+gestureData.selectWords;}GM_openInTab(gestureData.selectWords);', '打开图片':'GM_openInTab(gestureData.touchEle.src);', '百度搜图':'GM_openInTab("https://graph.baidu.com/details?isfromtusoupc=1&tn=pc&carousel=0&promotion_name=pc_image_shituindex&extUiData%5bisLogoShow%5d=1&image="+gestureData.touchEle.src);', '前进10s':'videoPlayer.currentTime+=10;gestureData.tipBox.innerHTML="+10s ";gestureData.tipBox.style.display="block";setTimeout(()=>{gestureData.tipBox.style.display="none";},500);', '后退10s':'videoPlayer.currentTime-=10;gestureData.tipBox.innerHTML="-10s ";gestureData.tipBox.style.display="block";setTimeout(()=>{gestureData.tipBox.style.display="none";},500);', '增加倍速':'if(document.fullscreen){let playSpeed=videoPlayer.playbackRate;playSpeed+=(playSpeed<1.5) ? 0.25 : 0.5;gestureData.tipBox.innerHTML="×"+playSpeed+" ∞ ";gestureData.tipBox.style.display="block";videoPlayer.playbackRate=playSpeed;setTimeout(()=>{gestureData.tipBox.style.display="none";},500)}', '减小倍速':'if(document.fullscreen){let playSpeed=videoPlayer.playbackRate;playSpeed-=(playSpeed>1.5) ? 0.5 : ((playSpeed>0.25) ? 0.25 : 0);gestureData.tipBox.innerHTML="×"+playSpeed+" ∞ ";gestureData.tipBox.style.display="block";videoPlayer.playbackRate=playSpeed;setTimeout(()=>{gestureData.tipBox.style.display="none";},500)}', '快进播放':'gestureData.playSpeed=videoPlayer.playbackRate;videoPlayer.playbackRate=10;gestureData.tipBox.innerHTML="×10 ";gestureData.tipBox.style.display="block";', '停止快进':'videoPlayer.playbackRate=gestureData.playSpeed;gestureData.tipBox.style.display="none";', '快退播放':'gestureData.videoTimer=setInterval(()=>{--videoPlayer.currentTime;},100);gestureData.tipBox.innerHTML="- ×10 ";gestureData.tipBox.style.display="block";', '停止快退':'clearInterval(gestureData.videoTimer);gestureData.tipBox.style.display="none";', '增加音量':'if(document.fullscreen){videoPlayer.muted=false;gestureData.tipBox.innerHTML=parseInt(videoPlayer.volume*100)+"%";gestureData.tipBox.style.display="block";let lastY=gestureData.touchEnd.screenY;gestureData.videoTimer=setInterval(()=>{if(lastY-gestureData.touchEnd.screenY){let tempVolume=videoPlayer.volume+(lastY-gestureData.touchEnd.screenY)/100;videoPlayer.volume=(tempVolume>1) ? 1 : ((tempVolume<0) ? 0 : tempVolume);gestureData.tipBox.innerHTML=parseInt(videoPlayer.volume*100)+"%";lastY=gestureData.touchEnd.screenY;}},50);}', '关闭增加音量':'clearInterval(gestureData.videoTimer);gestureData.tipBox.style.display="none";', '减少音量':'if(document.fullscreen){videoPlayer.muted=false;gestureData.tipBox.innerHTML=parseInt(videoPlayer.volume*100)+"%";gestureData.tipBox.style.display="block";let lastY=gestureData.touchEnd.screenY;gestureData.videoTimer=setInterval(()=>{if(lastY-gestureData.touchEnd.screenY){let tempVolume=videoPlayer.volume+(lastY-gestureData.touchEnd.screenY)/100;videoPlayer.volume=(tempVolume>1) ? 1 : ((tempVolume<0) ? 0 : tempVolume);gestureData.tipBox.innerHTML=parseInt(videoPlayer.volume*100)+"%";lastY=gestureData.touchEnd.screenY;}},50);}', '关闭减少音量':'clearInterval(gestureData.videoTimer);gestureData.tipBox.style.display="none";', '右滑进度':'gestureData.tipBox.innerHTML=((videoPlayer.currentTime/60<10) ? "0" : "")+Math.floor(videoPlayer.currentTime/60)+" : "+((videoPlayer.currentTime%60<10) ? "0" : "")+Math.floor(videoPlayer.currentTime%60);gestureData.tipBox.style.display="block";let lastX=gestureData.touchEnd.screenX;gestureData.videoTimer=setInterval(()=>{if(gestureData.touchEnd.screenX-lastX){videoPlayer.currentTime+=(gestureData.touchEnd.screenX-lastX)*(1+Math.abs(gestureData.touchEnd.screenX-lastX)/10);lastX=gestureData.touchEnd.screenX;}gestureData.tipBox.innerHTML=((videoPlayer.currentTime/60<10) ? "0" : "")+Math.floor(videoPlayer.currentTime/60)+" : "+((videoPlayer.currentTime%60<10) ? "0" : "")+Math.floor(videoPlayer.currentTime%60);},50);', '关闭右滑进度':'clearInterval(gestureData.videoTimer);gestureData.tipBox.style.display="none";', '左滑进度':'gestureData.tipBox.innerHTML=((videoPlayer.currentTime/60<10) ? "0" : "")+Math.floor(videoPlayer.currentTime/60)+" : "+((videoPlayer.currentTime%60<10) ? "0" : "")+Math.floor(videoPlayer.currentTime%60);gestureData.tipBox.style.display="block";let lastX=gestureData.touchEnd.screenX;gestureData.videoTimer=setInterval(()=>{if(gestureData.touchEnd.screenX-lastX){videoPlayer.currentTime+=(gestureData.touchEnd.screenX-lastX)*(1+Math.abs(gestureData.touchEnd.screenX-lastX)/10);lastX=gestureData.touchEnd.screenX;}gestureData.tipBox.innerHTML=((videoPlayer.currentTime/60<10) ? "0" : "")+Math.floor(videoPlayer.currentTime/60)+" : "+((videoPlayer.currentTime%60<10) ? "0" : "")+Math.floor(videoPlayer.currentTime%60);},50);', '关闭左滑进度':'clearInterval(gestureData.videoTimer);gestureData.tipBox.style.display="none";' }, settings={ '滑动距离':0.5, '文字手势':true, '图片手势':true, '视频手势':true }; //存储数据读取 gesture=GM_getValue('gesture',gesture); pathFn=GM_getValue('pathFn',pathFn); settings=GM_getValue('settings',settings); //脚本常量 const gestureData={},limit=(((screen.width>screen.height) ? screen.height : screen.width)*0.5*settings['滑动距离'])**2,_attachShadow=Element.prototype.attachShadow; /*手势功能模块*/ //手指滑动变量 let path='',_touch=null,timeSpan=0,pressTime=0,raiseTime=0,slideTime=0,lastTime=0,_sumXY=0,slideLimit=0,fingersNum=0,gestureTimer=0,isOK=0,isClick=0,isPushing=0,moveTime=0,moveTimer=0; //修改Trusted-Types策略 window.trustedTypes?.createPolicy('default',{createHTML:string=>string,createScript:string=>string,createScriptURL:string=>string}); //手势执行 function runCode(code){ try{eval(code);} catch(error){ if(error.toString().indexOf('unsafe-eval')>-1){ if(!window.evalTool){ window.evalTool=(()=>{ let script=document.createElement('script'); function thisParams(){ this.window.close=window.close; this.GM_setValue=GM_setValue; this.GM_getValue=GM_getValue; this.GM_openInTab=GM_openInTab; this.GM_setClipboard=GM_setClipboard; this.runCode=runCode; this.runGesture=runGesture; this.videoFullScreen=videoFullScreen; this.findVideoBox=findVideoBox; this.addStyle=addStyle; this.openSet=openSet; this.gestureData=gestureData; this.path=path; this.videoPlayer=videoPlayer; } return (js)=>{ thisParams(); script.remove(); script=document.createElement('script'); script.innerHTML='try{'+js+'}catch(error){alert("“"+path+"” 手势执行脚本错误:\\n"+error+" !");}'; document.body.appendChild(script); } })(); window.evalTool('window.addEventListener("popstate",()=>{clearTimeout(gestureData.backTimer);},true);window.addEventListener("beforeunload",()=>{clearTimeout(gestureData.backTimer);},true);'); } window.evalTool(code); } else{alert('“'+path+'” 手势执行脚本错误:\n'+error+' !');} } } function runFrame(_path){ let code=pathFn[gesture[_path]]; if(top==self || /^[TIV]/.test(_path)){runCode(code);} else{ if(code.indexOf('/*ONLY TOP*/')<0){runCode(code);} if(/\/\*(ONLY|WITH) TOP\*\//.test(code)){ gestureData.touchStart=copyTouch(gestureData.touchStart); gestureData.touchEnd=copyTouch(gestureData.touchEnd); GM_setValue('gestureData',gestureData); GM_setValue('iframeUrl',location.href); GM_setValue('runPath',path);isPushing=1; } } } function runGesture(pathStr=''){ if(gesture[path]){ runFrame(path); path=pathStr; }else if(gesture[path.slice(1)] && /^[TIV]/.test(path)){ runFrame(path.slice(1)); path=pathStr; } } //手指按下 function touchStart(e){ clearTimeout(gestureTimer); fingersNum=e.touches.length;if(fingersNum>1){return;} let calcX=(e.changedTouches[0].screenX-gestureData.touchEnd?.screenX)**2,calcY=(e.changedTouches[0].screenY-gestureData.touchEnd?.screenY)**2, sumXY=calcX+calcY,nowTime=Date.now();timeSpan=nowTime-raiseTime; if(timeSpan>50 || sumXY>2500){//断触判断 pressTime=slideTime=nowTime; isPushing=_sumXY=0;isOK=isClick=1; _touch=e.changedTouches[0]; if(timeSpan>200 || sumXY>10000){ path='';slideLimit=limit; gestureData.touchEle=e.target; gestureData.touchStart=_touch; gestureData.selectWords=window.getSelection().toString(); if(gestureData.selectWords && settings['文字手势']){path='T';} else if(videoPlayer && settings['视频手势']){ let videoRect=videoPlayer.getBoundingClientRect(),offsetX=videoPlayer.offsetLeft,offsetY=videoPlayer.offsetTop-((typeof fullscreenEle=='object') ? videoRect.height*0.1 : 0); if(gestureData.touchStart.clientX>(videoRect.x-offsetX) && gestureData.touchStart.clientX<(videoRect.x+videoRect.width+offsetX) && gestureData.touchStart.clientY>(videoRect.y-offsetY) && gestureData.touchStart.clientY<(videoRect.y+videoRect.height+offsetY)){path='V';} } } }else if(isClick){path=path.slice(0,-1);}window.noClick?.remove(); gestureTimer=setTimeout(()=>{slideTime=isOK=isClick=0;if(!/[●○▽]$/.test(path)){let _path=path+'○';path+='●';if(gesture[_path]){runGesture(_path);}else{runGesture(path);}}},(400+slideTime-nowTime)); } //手指滑动 function touchMove(e){ let nowTime=Date.now(); if((nowTime-lastTime)<16 || fingersNum>1){return;} clearTimeout(gestureTimer);clearTimeout(moveTimer); gestureData.touchEnd=e.changedTouches[0]; let calcX=(gestureData.touchEnd.screenX-_touch.screenX)**2,calcY=(gestureData.touchEnd.screenY-_touch.screenY)**2, sumXY=calcX+calcY,lastIcon=path.slice(-1), diffXY=(sumXY>_sumXY) ? sumXY-_sumXY : _sumXY-sumXY; lastTime=nowTime;_sumXY=sumXY; if(diffXY>4 && !/[○▽]/.test(lastIcon)){ isOK=isClick=0;slideTime=nowTime; let direction=(calcX>calcY) ? ((gestureData.touchEnd.screenX>_touch.screenX) ? '→' : '←') : ((gestureData.touchEnd.screenY>_touch.screenY) ? '↓' : '↑'); if(lastIcon==direction || sumXY>slideLimit){ if(lastIcon!=direction && (timeSpan>200 || timeSpan<84 || 'TIV◆'.indexOf(lastIcon)>-1)){path+=direction;slideLimit*=(slideLimit{moveTime=0;let _path=path;path+='▼';if(gesture[_path+'▽']){runGesture(_path+'▽');}else{runGesture(_path);}path=path.replace('▼','');},400+moveTime-nowTime);} } } if(slideTime){gestureTimer=setTimeout(()=>{slideTime=isOK=isClick=0;if(!/[●○▽]/.test(lastIcon)){_sumXY=0;_touch=gestureData.touchEnd;let _path=path+'○';path+='●';if(gesture[_path]){runGesture(_path);}else{runGesture(path);}}},(400+slideTime-nowTime));} if(top!=self && isPushing){gestureData.touchEnd=copyTouch(gestureData.touchEnd);GM_setValue('gestureData',gestureData);} } //手指抬起 function touchEnd(e){ clearTimeout(gestureTimer);clearTimeout(moveTimer); gestureData.touchEnd=e.changedTouches[0]; raiseTime=Date.now();isPushing=0;setTimeout(iframeLock,16); if(/[○▽]$/.test(path)){raiseTime=0;runGesture();return;} if(fingersNum>1){raiseTime=0;return;} if(isOK){gestureTimer=setTimeout(runGesture,199);} if(isClick){ let calcX=(gestureData.touchEnd.screenX-_touch.screenX)**2,calcY=(gestureData.touchEnd.screenY-_touch.screenY)**2; if((calcX+calcY)>16){isClick=0;return;} path+='◆';if(/^V◆◆$|^T/.test(path)){e.stopPropagation();window.noClick=addStyle('*{pointer-events:none;}');} } } /*视频功能模块*/ //video标签变量 let videoEle=document.getElementsByTagName('video'),_videoEle=[],videoPlayer=null,oriType='portrait-primary',lockOriType='landscape-primary',isLock=0,resizeTimer=0,fullscreenEle=0; //videoPlayer赋值 function setVideo(player){ let _video=player.target || player; if(_video==videoPlayer){return;} videoPlayer=_video; videoPlayer.parentNode.appendChild(gestureData.tipBox); videoOriLock(); } //video方向锁定 function videoOriLock(){ if(videoPlayer.error || !videoPlayer.offsetWidth){return;} if(!videoPlayer.videoWidth){setTimeout(videoOriLock,100);return;} if(videoPlayer.videoWidth>videoPlayer.videoHeight){isLock=1;}else{isLock=0;} if(top!=self){window.isShow=1;GM_setValue('isLock',isLock);} } //iframe锁定 function iframeLock(){ if(top!=self && videoPlayer && !window.isShow){window.isShow=1;GM_setValue('isLock',isLock);} } //video播放事件绑定 function videoBind(){ _videoEle=[...videoEle]; for(let Ti=0,len=videoEle.length;Ti=videoBox.children.length){videoBox=parentEle;} childStyle=getComputedStyle(parentEle); if(parentEle.offsetParent==parentEle.parentNode){ childWidth=Math.round(parentEle.offsetWidth+parseFloat(childStyle.marginLeft)+parseFloat(childStyle.marginRight)) || childWidth; childHeight=Math.round(parentEle.offsetHeight+parseFloat(childStyle.marginTop)+parseFloat(childStyle.marginBottom)) || childHeight; }else{ childWidth=Math.round(parentEle.offsetLeft+parentEle.offsetWidth+parseFloat(childStyle.marginRight)+(parseFloat(childStyle.right) || 0)) || childWidth; childHeight=Math.round(parentEle.offsetTop+parentEle.offsetHeight+parseFloat(childStyle.marginBottom)+(parseFloat(childStyle.bottom) || 0)) || childHeight; } parentEle=parentEle.parentNode; } videoBox.isVideoBox=1; videoPlayer.videoBox=videoBox; return videoBox; } //陀螺仪事件 let regGYRO=()=>{ let runTime=0; window.addEventListener('deviceorientation',async (e)=>{ let nowTime=Date.now(); if((nowTime-runTime)<500 || !isLock || fullscreenEle==1){return;} runTime=nowTime; let oriHgamma=e.gamma, oriHbeta=(e.beta>0) ? e.beta : -e.beta; if((oriHbeta<70 || oriHbeta>110) && (oriHgamma<-20 || oriHgamma>20)){ lockOriType=((oriHbeta<70 && oriHgamma<-20) || (oriHbeta>110 && oriHgamma>20)) ? 'landscape-primary' : 'landscape-secondary'; } if(document.fullscreen){if(oriType!=lockOriType){oriType=lockOriType;await screen.orientation.lock(lockOriType)?.catch();}} else{oriType='portrait-primary';} },true); }, //全屏检测事件 regRESIZE=()=>{ let videoCss=null; window.addEventListener('resize',async ()=>{ clearTimeout(resizeTimer);resizeTimer=setTimeout(()=>{resizeTimer=0;},200); if(document.fullscreen && !fullscreenEle){ fullscreenEle=document.fullscreenElement;let tagName=fullscreenEle.nodeName; if(tagName=='IFRAME'){fullscreenEle=1;return;} videoCss=addStyle('*:has(> video){height:100% !important;width:100% !important;padding:0 !important;}video{position:fixed !important;width:100% !important;height:100% !important;left:0 !important;top:0 !important;margin:0 !important;padding:0 !important;transform:none !important;object-fit:contain !important;}'); let srcFindVideo=fullscreenEle.getElementsByTagName('video'),srcVideo=(tagName=='VIDEO') ? fullscreenEle : srcFindVideo[0]; if(!fullscreenEle.isVideoBox && (!srcVideo || srcFindVideo.length>1 || (srcVideo.parentNode.offsetWidth*srcVideo.parentNode.offsetHeight/fullscreenEle.offsetWidth/fullscreenEle.offsetHeight)<0.9)){fullscreenEle=1;videoCss?.remove();} if(top!=self){GM_setValue('fullscreenEle',fullscreenEle);} if(fullscreenEle==1){return;}fullscreenEle.isVideoBox=1; if(srcVideo!=videoPlayer){videoPlayer?.pause();setVideo(srcVideo);} if(isLock && oriType!=lockOriType && top==self){oriType=lockOriType;await screen.orientation.lock(lockOriType)?.catch();} }else if(fullscreenEle && !document.fullscreen){fullscreenEle=0;oriType='portrait-primary';videoCss?.remove();} },true); }; /*工具方法模块*/ //添加样式表 function addStyle(css){ let style=document.createElement('style'); style.innerHTML=css; document.head.appendChild(style); return style; } //复制坐标对象 function copyTouch(oldObj){ let newObj={}; for(let Ti in oldObj){ if(Ti=='target'){continue;} newObj[Ti]=oldObj[Ti]; } return newObj; } //设置shadow-root (open) Element.prototype.attachShadow=function(){ if(!window.shadowList){window.shadowList=[];} let shadowRoot=_attachShadow.call(this,{mode:'open'}); window.shadowList.push(shadowRoot); return shadowRoot; } //页面加载检测 function loadCheck(){ if(window.shadowList){//检测shadowRoot中的视频 videoEle=[...document.getElementsByTagName('video')]; for(let Ti=0,len=window.shadowList.length;Ti{resizeTimer=-1;},true); //tip操作提示 gestureData.tipBox=document.createElement('div'); gestureData.tipBox.style.cssText='width:100px;height:50px;position:absolute;text-align:center;top:calc(50% - 25px);left:calc(50% - 50px);display:none;color:#1e87f0;font-size:22px;line-height:50px;background-color:#fff;border-radius:20px;font-family:system-ui;z-index:2147483647;'; } videoBind(); } } //手势功能设置UI function openSet(){ let gestureName='',gesturePath='',gestureBox=document.createElement('div'),pathEle=null,clickTimer=0; //页面生成 addStyle('*{overflow:hidden !important;}'+ '#gestureBox{background-color:#fff;width:100%;height:100%;position:fixed;padding:0;margin:0;top:0;left:0;overflow-y:auto !important;z-index:2147483640;}'+ '#gestureBox *{font-family:system-ui;margin:0;padding:0;text-align:center;font-size:5vmin;line-height:12vmin;user-select:none !important;transform:none;}'+ '#gestureBox ::placeholder{color:#999;font-size:2.5vmin;line-height:6vmin;}'+ '#gestureBox h1{width:60%;height:12vmin;color:#0074d9;background-color:#dee6ef;margin:3vmin auto;border-radius:12vmin;box-shadow:0.9vmin 0.9vmin 3vmin #dfdfdf;}'+ '#gestureBox #addGesture{width:14vmin;height:14vmin;margin:3vmin auto;line-height:14vmin;background-color:#dee6ef;color:#032e58;font-size:7.5vmin;border-radius:15vmin;box-shadow:0.3vmin 0.3vmin 1.5vmin #dfdfdf;}'+ '#gestureBox .gestureLi{height:18vmin;width:100%;border-bottom:0.3vmin solid #dfdfdf;}'+ '#gestureBox .gestureLi p{margin:3vmin 0 0 1%;width:38%;height:12vmin;border-left:1.8vmin solid;color:#ffb400;background-color:#fff1cf;float:left;white-space:nowrap;text-overflow:ellipsis;text-shadow:0.3vmin 0.3vmin 3vmin #ffcb56;}'+ '#gestureBox .gestureLi .gesturePath{margin:3vmin 0 0 3%;float:left;width:38%;height:12vmin;background-color:#f3f3f3;color:#000;box-shadow:0.3vmin 0.3vmin 1.5vmin #ccc9c9;border-radius:3vmin;white-space:nowrap;text-overflow:ellipsis;}'+ '#gestureBox .gestureLi .delGesture{margin:3vmin 2% 0 0;width:15vmin;height:12vmin;float:right;color:#f00;text-decoration:line-through;}'+ '#gestureBox #revisePath{background-color:rgba(0,0,0,0.7);width:100%;height:100%;position:fixed;top:0;left:0;z-index:2147483641;display:none;color:#000;}'+ '#gestureBox #revisePath span{width:15vmin;height:15vmin;font-size:12.5vmin;line-height:15vmin;position:absolute;}'+ '#gestureBox #revisePath div{color:#3339f9;position:absolute;width:30%;height:12vmin;font-size:10vmin;bottom:15%;}'+ '#gestureBox #revisePath p{color:#3ba5d8;position:absolute;top:15%;font-size:10vmin;height:12vmin;width:100%;}'+ '#gestureBox #revisePath #path{top:40%;color:#ffee03;height:100%;word-wrap:break-word;font-size:15vmin;line-height:18vmin;}'+ '#gestureBox #editGesture{overflow-y:auto !important;background-color:#fff;width:100%;height:100%;position:fixed;top:0;left:0;z-index:2147483641;display:none;color:#000;}'+ '#gestureBox #editGesture p{color:#3339f9;font-size:7.5vmin;text-align:left;margin:6vmin 0 0 9vmin;width:100%;height:9vmin;line-height:9vmin;}'+ '#gestureBox #editGesture #gestureName{margin-top:6vmin;width:80%;height:12vmin;color:#000;border:0.3vmin solid #dadada;border-radius:3vmin;text-align:left;padding:0 3vmin;}'+ '#gestureBox #editGesture .label_box>label{display:inline-block;margin-top:6vmin;position:relative;}'+ '#gestureBox #editGesture .label_box>label>input{position:absolute;top:0;left:-6vmin;}'+ '#gestureBox #editGesture .label_box>label>div{width:20vmin;border:#ddd solid 0.3vmin;height:12vmin;color:#666;position:relative;}'+ '#gestureBox #editGesture .label_box>label>input:checked + div{border:#d51917 solid 0.3vmin;color:#d51917;}'+ '#gestureBox #editGesture .label_box>label>input + div:after{top:auto;left:auto;bottom:-3vmin;right:0;transition:none;}'+ '#gestureBox #editGesture .label_box>label>input:checked + div:after{content:"";display:block;border:none;width:6vmin;height:6vmin;background-color:#d51917;transform:skewY(-45deg);position:absolute;z-index:2147483642;}'+ '#gestureBox #editGesture .label_box>label>input:checked + div:before{content:"";display:block;width:0.9vmin;height:2.4vmin;border-right:#fff solid 0.6vmin;border-bottom:#fff solid 0.6vmin;transform:rotate(35deg);position:absolute;bottom:0.6vmin;right:1.2vmin;z-index:2147483643;}'+ '#gestureBox #editGesture #pathFn{overflow-y:auto !important;width:80%;margin-top:6vmin;height:40%;text-align:left;line-height:6vmin;padding:3vmin;border:0.3vmin solid #dadada;border-radius:3vmin;}'+ '#gestureBox #editGesture button{width:30vmin;height:15vmin;font-size:7.5vmin;line-height:15vmin;display:inline-block;color:#fff;background-color:#2866bd;margin:6vmin 3vmin 0 3vmin;border:none;}'+ '#gestureBox #settingsBox{overflow-y:auto !important;background-color:#fff;width:100%;height:100%;position:fixed;top:0;left:0;z-index:2147483641;display:none;color:#000;}'+ '#gestureBox #settingsBox p{color:#3339f9;text-align:left;margin:9vmin 0 0 9vmin;float:left;height:6vmin;line-height:6vmin;clear:both;}'+ '#gestureBox #settingsBox .slideRail{overflow:initial !important;width:55vmin;background-color:#a8a8a8;float:left;margin:12vmin 0 0 3vmin;height:0.6vmin;position:relative;}'+ '#gestureBox #settingsBox .slideRail .slideButton{line-height:9vmin;color:#fff;background-color:#2196f3;width:9vmin;height:9vmin;border-radius:9vmin;font-size:4vmin;position:absolute;top:-4.5vmin;left:-4.5vmin;box-shadow:0.3vmin 0.3vmin 1.8vmin #5e8aee;}'+ '#gestureBox #settingsBox .switch{position:relative;display:inline-block;width:18vmin;height:9vmin;float:left;margin:7.5vmin 42% 0 3vmin;}'+ '#gestureBox #settingsBox .switch input{display:none;}'+ '#gestureBox #settingsBox .slider{border-radius:9vmin;position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ccc;transition:0.4s;}'+ '#gestureBox #settingsBox .slider:before{border-radius:50%;position:absolute;content:"";height:7.5vmin;width:7.5vmin;left:0.6vmin;bottom:0.6vmin;background-color:white;transition:0.4s;}'+ '#gestureBox #settingsBox input:checked + .slider{background-color:#2196F3;}'+ '#gestureBox #settingsBox input:checked + .slider:before{transform:translateX(9vmin);}'+ '#gestureBox #settingsBox #saveSettings{display:block;clear:both;width:30vmin;height:15vmin;font-size:7.5vmin;line-height:15vmin;color:#fff;background-color:#2866bd;border:none;margin:12vmin 0 0 calc(50% - 15vmin);float:left;}'); gestureBox.id='gestureBox'; document.body.appendChild(gestureBox); gestureBox.innerHTML='

手势轨迹设置

+
'+ '
'+ '

请滑动手指

清除
保存
'+ '

手势名称:

'+ '

手势类型:

'+ '

手势执行脚本:

'+ '
'+ '

功能开关设置

'; pathEle=document.getElementById('path'); //编辑手势 function editGesture(){ gestureName=this.parentNode.getAttribute('name'); if(['打开设置','视频全屏','手势穿透'].indexOf(gestureName)>-1){alert('该手势脚本无法修改!');return;} gesturePath=this.parentNode.getAttribute('path'); let selectType=(/^[TIV]/.test(gesturePath)) ? gesturePath.slice(0,1) : 'GG'; document.getElementById(selectType).click(); document.getElementById('gestureName').value=gestureName; document.getElementById('pathFn').value=pathFn[gestureName]; document.getElementById('editGesture').style.display='block'; } //修改路径 function revisePath(){ gestureName=this.parentNode.getAttribute('name'); gesturePath=this.parentNode.getAttribute('path'); pathEle.innerHTML=''; window.removeEventListener('touchmove',touchMove,true); window.removeEventListener('touchend',touchEnd,true); document.getElementById('revisePath').style.display='block'; } //删除手势 function delGesture(){ gestureName=this.parentNode.getAttribute('name'); if(['打开设置','视频全屏','手势穿透'].indexOf(gestureName)>-1){alert('该手势无法删除!');return;} gesturePath=this.parentNode.getAttribute('path'); delete pathFn[gestureName]; delete gesture[gesturePath]; GM_setValue('pathFn',pathFn); GM_setValue('gesture',gesture); init(); } //滑动条 function silideBar(e){ fingersNum=2; let diffX=e.changedTouches[0].clientX-gestureData.touchStart.clientX, leftPX=parseFloat(this.style.left)+diffX,vmin=this.offsetWidth/2; leftPX=(leftPX<-vmin) ? -vmin : ((leftPX>(this.parentNode.offsetWidth-vmin)) ? (this.parentNode.offsetWidth-vmin) : leftPX); this.style.left=leftPX+'px'; leftPX=(leftPX+vmin)/this.parentNode.offsetWidth; this.innerHTML=leftPX.toFixed(2); gestureData.touchStart=e.changedTouches[0]; } //界面初始化 function init(){ document.getElementById('gestureUL').innerHTML=''; for(gestureName in pathFn){ gesturePath=''; for(let Ti in gesture){ if(gesture[Ti]==gestureName){gesturePath=Ti;break;} } document.getElementById('gestureUL').innerHTML+='

'+gestureName+'

'+gesturePath+'
删除
'; } //操作绑定 let gestureEle=document.querySelectorAll('#gestureBox .gestureLi p'); for(let Ti=0,len=gestureEle.length;Ti{ gestureName=gesturePath=''; document.getElementById('GG').click(); document.getElementById('gestureName').value=''; document.getElementById('pathFn').value=''; document.getElementById('editGesture').style.display='block'; },true); //保存手势 document.getElementById('saveGesture').addEventListener('click',()=>{ if(!document.getElementById('gestureName').value){alert('请输入手势名称!');return;} if(pathFn[document.getElementById('gestureName').value] && gestureName!=document.getElementById('gestureName').value){alert('该手势名称已被占用!');return;} delete pathFn[gestureName]; delete gesture[gesturePath]; let typeEle=document.getElementsByName('gestureType'); for(let Ti=0,len=typeEle.length;Ti{ document.getElementById('editGesture').style.display='none'; },true); //路径修改事件 document.getElementById('revisePath').addEventListener('touchstart',()=>{ if(fingersNum>1){return;} clearTimeout(gestureTimer); gestureTimer=setTimeout(()=>{slideTime=isClick=0;if(!/[●○▼▽]$/.test(pathEle.innerHTML)){pathEle.innerHTML+='●';}},(400+slideTime-Date.now())); },true); document.getElementById('revisePath').addEventListener('touchmove',(e)=>{ let nowTime=Date.now(); if((nowTime-lastTime)<16 || fingersNum>1){return;} clearTimeout(gestureTimer);clearTimeout(moveTimer); gestureData.touchEnd=e.changedTouches[0]; let calcX=(gestureData.touchEnd.screenX-_touch.screenX)**2,calcY=(gestureData.touchEnd.screenY-_touch.screenY)**2, sumXY=calcX+calcY,lastIcon=pathEle.innerHTML.slice(-1), diffXY=(sumXY>_sumXY) ? sumXY-_sumXY : _sumXY-sumXY; lastTime=nowTime;_sumXY=sumXY; if(diffXY>4 && !/[○▼▽]/.test(lastIcon)){ isClick=0;slideTime=nowTime; let direction=(calcX>calcY) ? ((gestureData.touchEnd.screenX>_touch.screenX) ? '→' : '←') : ((gestureData.touchEnd.screenY>_touch.screenY) ? '↓' : '↑'); if(lastIcon==direction || sumXY>limit){ if(lastIcon!=direction){pathEle.innerHTML+=direction;moveTime=nowTime;} _sumXY=0;_touch=gestureData.touchEnd; if(moveTime){moveTimer=setTimeout(()=>{moveTime=0;pathEle.innerHTML+='▼';},400+moveTime-nowTime);} } } if(slideTime){gestureTimer=setTimeout(()=>{slideTime=isClick=0;if(!/[●○▼▽]/.test(lastIcon)){_sumXY=0;_touch=gestureData.touchEnd;pathEle.innerHTML+='●';}},(400+slideTime-nowTime));} },true); document.getElementById('revisePath').addEventListener('touchend',(e)=>{ clearTimeout(gestureTimer);clearTimeout(moveTimer); if(!isClick || fingersNum>1){return;} let lastIcon=pathEle.innerHTML.slice(-1); if((pressTime-raiseTime)<200){ raiseTime=0; if(lastIcon=='●'){clearTimeout(clickTimer);pathEle.innerHTML=pathEle.innerHTML.slice(0,-1)+'○';} else if(lastIcon=='○'){clearTimeout(clickTimer);pathEle.innerHTML=pathEle.innerHTML.slice(0,-1)+'●';} else if(lastIcon=='▼'){clearTimeout(clickTimer);pathEle.innerHTML=pathEle.innerHTML.slice(0,-1)+'▽';} else if(lastIcon=='▽'){clearTimeout(clickTimer);pathEle.innerHTML=pathEle.innerHTML.slice(0,-1)+'▼';} else{pathEle.innerHTML+='◆';} }else{ raiseTime=Date.now(); clickTimer=setTimeout(()=>{if(!/[○▼▽]/.test(lastIcon)){pathEle.innerHTML+='◆';}},400); } }); //清除路径 document.getElementById('clearPath').addEventListener('touchend',(e)=>{ e.stopPropagation();e.preventDefault(); clearTimeout(gestureTimer); if(!isClick || fingersNum>1){return;} if((pressTime-raiseTime)<200){ raiseTime=0; pathEle.innerHTML=''; }else{ raiseTime=Date.now(); pathEle.innerHTML=pathEle.innerHTML.slice(0,-1); } }); //保存修改路径 document.getElementById('cancleRevise').addEventListener('touchend',(e)=>{ e.stopPropagation();e.preventDefault(); clearTimeout(gestureTimer); if(!isClick || fingersNum>1){return;} if(pathEle.innerHTML){ if(gestureName=='视频全屏' && pathEle.innerHTML.slice(-1)!='◆'){alert('视频全屏需要以◆结尾!');return;} if(gesture[pathEle.innerHTML]=='手势穿透'){alert('路径与"手势穿透"功能冲突!');return;} if(/^[TIV]/.test(gesturePath)){pathEle.innerHTML=gesturePath.slice(0,1)+pathEle.innerHTML;} delete gesture[gesturePath]; if(gesture[pathEle.innerHTML]){ let pathTXT=((/^[TIV]/.test(gesturePath)) ? gesturePath.slice(0,1) : '')+'['+gesture[pathEle.innerHTML]+']'; gesture[pathTXT]=gesture[pathEle.innerHTML]; } gesture[pathEle.innerHTML]=gestureName; GM_setValue('gesture',gesture); init(); } window.addEventListener('touchmove',touchMove,{capture:true,passive:true}); window.addEventListener('touchend',touchEnd,{capture:true,passive:true}); document.getElementById('revisePath').style.display='none'; }); //打开功能开关设置 document.getElementById('openSettings').addEventListener('click',()=>{ gestureBox.style.cssText='overflow-y:hidden !important'; document.getElementById('settingsBox').style.display='block'; let settingList=document.getElementById('settingList'); settingList.innerHTML=''; for(let Ti in settings){ settingList.innerHTML+='

'+Ti+':

'; if(typeof(settings[Ti])=='boolean'){ settingList.innerHTML+=''; }else if(typeof(settings[Ti])=='number'){ settingList.innerHTML+='
'; let slideButton=document.getElementById(Ti), leftPX=slideButton.parentNode.offsetWidth*settings[Ti]-slideButton.offsetWidth/2; slideButton.style.left=leftPX+'px'; slideButton.innerHTML=settings[Ti].toFixed(2); } } let slideList=document.getElementsByClassName('slideButton'); for(let Ti=0,len=slideList.length;Ti{ gestureBox.style.cssText=''; for(let Ti in settings){ if(typeof(settings[Ti])=='boolean'){ settings[Ti]=document.getElementById(Ti).checked; }else if(typeof(settings[Ti])=='number'){ settings[Ti]=parseFloat(document.getElementById(Ti).innerHTML); } } GM_setValue('settings',settings); document.getElementById('settingsBox').style.display='none'; },true); } /*功能补充模块*/ let iframeEle=document.getElementsByTagName('iframe'); //检测事件是否添加 (function regEvent(){if(getComputedStyle(document.documentElement).userSelect!='text'){ if(top==self){ //禁止缩放 if((document.documentElement.offsetWidth/document.documentElement.scrollWidth)>0.95 && window.outerWidth/window.innerWidth>0.95){ let meta=document.querySelector('meta[name="viewport"]'); if(meta){meta.content+=',user-scalable=no';} else{ meta=document.createElement('meta'); meta.name='viewport';meta.content='user-scalable=no'; document.head.appendChild(meta); } } //清除后退关闭定时器 window.addEventListener('popstate',()=>{clearTimeout(gestureData.backTimer);},true); window.addEventListener('beforeunload',()=>{clearTimeout(gestureData.backTimer);},true); //iframe手势在顶级页面执行 GM_addValueChangeListener('runPath',(name,old_value,new_value,remote)=>{ if(remote && !document.hidden && new_value){ isPushing=1; let _gestureData=GM_getValue('gestureData',gestureData),iframeUrl=GM_getValue('iframeUrl'),iframe=iframeEle[0]; for(let Ti=0,len=iframeEle.length;Ti{ if(remote && !document.hidden && isPushing){ let ifrRect=gestureData.touchEle.getBoundingClientRect(); gestureData.touchEnd=new_value.touchEnd; gestureData.touchEnd.target=gestureData.touchEle; gestureData.touchEnd.clientX=new_value.touchEnd.clientX+ifrRect.x;gestureData.touchEnd.clientY=new_value.touchEnd.clientY+ifrRect.y; gestureData.touchEnd.pageX=gestureData.touchEnd.clientX;gestureData.touchEnd.pageY=gestureData.touchEnd.clientY; } }); //iframe全屏方向 GM_addValueChangeListener('fullscreenEle',async (name,old_value,new_value,remote)=>{ if(remote && !document.hidden && new_value){ fullscreenEle=new_value; if(isLock && oriType!=lockOriType && fullscreenEle!=1){oriType=lockOriType;await screen.orientation.lock(lockOriType)?.catch();} GM_setValue('fullscreenEle',0); } }); }else{ //iframe视频全屏 GM_addValueChangeListener('fullscreen',async (name,old_value,new_value,remote)=>{ if(remote && !document.hidden && window.isShow){ GM_setClipboard(videoPlayer.src); await findVideoBox().requestFullscreen()?.catch(); } }); } //iframe锁定 GM_addValueChangeListener('isLock',(name,old_value,new_value,remote)=>{ if(remote && !document.hidden && new_value>-1){ if(top==self){ if(regGYRO){regGYRO();regGYRO=null;} if(regRESIZE){regRESIZE();regRESIZE=null;} isLock=new_value; GM_setValue('isLock',-1); }else{window.isShow=0;} } }); //解除选中限制 addStyle('*{user-select:text !important;}'); //加载检测 (()=>{let checkTimer=0, observer=new MutationObserver((mutations)=>{ if(checkTimer){return;} checkTimer=setTimeout(()=>{checkTimer=0;loadCheck();},200); }); observer.observe(document,{childList:true,subtree:true});})(); //手势事件注册 window.addEventListener('touchstart',touchStart,{capture:true,passive:true}); window.addEventListener('touchmove',touchMove,{capture:true,passive:true}); window.addEventListener('touchend',touchEnd,{capture:true,passive:true});} //*页面加载完成停止检测 if(document.readyState!='complete'){setTimeout(regEvent,16);}})(); })();