// ==UserScript== // @name 手机端浏览器功能扩展(原yandex视频横屏插件) // @name:en Add additional functions to mobile browser // @description 手机端可装插件浏览器(如yandex,kiwi)添加额外的功能。例如:双击视频全屏,双击快速搜索,左右滑动视频进度,单手手势操作等。(手势如:↓↑回到顶部,↑↓回到底部,→←后退,←→前进,→↓关闭标签页,→↑重新打开页面等) // @description:en Add additional functions to mobile browser(like yandex browser).For example, double click to make video full screen , double click to search for selected text , slide to adjust video progress and sliding gesture by one hand. // @version 3.8.2 // @author L.Xavier // @namespace https://greasyfork.org/zh-CN/users/128493 // @include * // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant unsafeWindow // @grant window.close // @grant GM_openInTab // @grant GM_addValueChangeListener // @run-at document-start // @note 功能说明:1.全屏下强制为横屏观看,支持重力感应切换方向。 2.添加双击全屏功能。一些网站全屏按钮无法使视频进入全屏模式(例如m.bilibili.com),在视频播放状态下,双击全屏(支持双击退出全屏)。 3.视频拖动进度功能,左右滑调整视频播放进度。 4.添加手势操作,方便单手操作浏览器。现有手势,↓↑回到顶部,↑↓回到底部,→←后退,←→前进,←↓刷新页面,←↑新建空白页,→↓关闭标签页,→↑重新打开页面。在手势设置界面可自行添加修改手势功能。(↑→↓←打开手势设置界面) 5.新增双击功能。双击页面,若有选中文字则百度搜索该关键字,若选中文字为网址,则打开网址页面。 // @v1.1.0 2019-01-01 - 1.添加竖屏模式。有些视频更适宜在竖屏状态下观看,全屏状态下,手机竖直摆放时会重新回到竖屏模式。2.添加双击全屏功能。一些网站全屏按钮无法使视频进入全屏模式(例如m.bilibili.com),新增在视频播放状态下,双击全屏(支持双击退出全屏)。该功能需要关闭yandex浏览器的"忽略网站的禁止字体缩放请求"功能,不关闭双击会缩放网页。 // @v1.1.1 2019-01-02 - 优化了代码逻辑 // @v1.1.2 2019-01-12 - Yandex浏览器版本18.11.1(安卓自2018年12月26日,版本号979),切换竖屏会退出全屏状态,暂时取消竖屏模式,其他保持不变。不是该版本浏览器,有竖屏观看需要的可以安装本脚本历史版本1.1.1。 // @v1.2.1 2019-02-01 - 重写了一部分代码,使之使用更加人性化。重新添加竖屏观看模式。使用方法,摇一摇切换竖屏观看,再摇一摇可换回横屏观看(可调节灵敏度,SHAKE_THRESHOLD参数,越小越灵敏)。火狐浏览器(安卓)使用该版本,除了双击全屏,其他功能正常。 // @v1.2.2 2019-02-03 - 防止误操作,摇一摇调整为全屏下执行。 // @v1.2.3 2019-02-04 - 修复了摇一摇短时间内可能触发两次的问题。 // @v1.2.4 2019-02-09 - 修复了后加载视频,某些功能可能需要多次点击才能生效的问题。 // @v1.2.5 2019-02-13 - 调整视频标签查找逻辑和事件方法,添加了更新说明。 // @v2.0.0 2019-08-22 - 新增视频滑动进行前进后退功能;修复在已有视频标签下,重新请求视频源双击全屏无效问题。 // @v2.1.0 2019-08-24 - 完善视频滑动前进后退进度,添加进度时间小tip(可能不同手机会有位置不居中的问题,大家可以用bilibili的桌面版页面测试)。优化代码逻辑。 // @v3.0.0 2019-10-02 - 横屏播放调整为桌面版启动,旧版yandex无横屏的小伙伴可以更新版本或用本脚本旧版本。因显示优先级问题,去除视频时间tip。新添加手势功能,有爱的小伙伴可自行玩耍,添加新手势。现有手势,→↑回到顶部,→↓回到底部,→←后退,←→前进,←视频进度后退,→视频进度前进。 // @v3.1.0 2019-10-03 - 调整视频横屏播放逻辑,修复脚本与浏览器竖屏播放功能冲突问题。去除摇一摇竖屏功能。 // @v3.1.1 2019-10-03 - 调整手势灵敏度 // @v3.1.2 2019-10-03 - 修复语法错误导致的横屏逻辑失效问题 // @v3.2.0 2019-10-06 - 精简优化代码。调整视频滑动进度函数,使进度随滑动距离成指数增加,减小在短距离时的时间进度。调整手势功能代码逻辑,下个版本将添加手势功能设置UI,可自己添加修改手势操作。 // @v3.2.1 2019-10-15 - 修复因代码冲突导致的视频标签查找问题,调整查找视频标签逻辑。调整视频滑动进度判断逻辑。 // @v3.3.0 2019-11-05 - 添加手势设置UI界面(略粗糙,见谅),↑→↓←打开手势设置界面。添加并调整一部分功能代码。 // @v3.3.1 2019-11-09 - 修复未引用tampermonkey的功能模块而导致的功能无法使用,修改ajax生成视频的查找逻辑。 // @v3.3.2 2019-11-11 - 修复可能因视频自动播放而导致video标签获取不到的问题。 // @v3.3.3 2019-11-30 - 微调手势设置UI样式,使某些错版页样式一致。微调视频滑动进度快慢。 // @v3.4.0 2020-01-03 - 手势灵敏度更灵敏(减小滑动距离判断)。修复Ajax重新请求后无法找到当前播放视频问题。调整视频滑动进度函数。 // @v3.4.1 2020-01-03 - 因灵敏度太高容易造成误滑,故重新调整判断距离。 // @v3.5.0 2020-01-07 - 后退手势调整为当没有上一页时,关闭标签页。新增 “↑→↓”关闭其他页面 手势。 // @v3.5.1 2020-01-07 - 为修改过手势的小伙伴添加更新手势存储。 // @v3.6.0 2020-01-13 - 新增双击功能。双击页面,若有选中文字则百度搜索该关键字,若选中文字为网址,则打开网址页面。 // @v3.7.0 2020-01-13 - 调整双击事件的触发逻辑,使之在未限制缩放的网页或PC版网页下仍可生效。减小滑动距离判定,更加敏感,但手势路径长度为2的手势需要在600ms内完成动作,否则判定为无效。调整视频滑动进度函数。 // @v3.7.1 2020-01-14 - 修改简介说明文字。 // @v3.7.2 2020-01-15 - 调整手势触发时间。 // @v3.7.3 2020-01-16 - 时间限制调整为仅有↑↓和↓↑触发,其余手势皆无限制。 // @v3.7.4 2020-01-19 - 修复视频滑动的距离判定错误。 // @v3.8.0 2020-01-20 - 细化视频滑动时间,更加精准。要快速到片尾,请大力滑动。 // @v3.8.1 2020-01-20 - 将←→和→←加入时间限制。 // @v3.8.2 2020-01-22 - 滑动距离太短会在电脑版页面上容易误触,故重新调整。 // @downloadURL none // ==/UserScript== (function() { 'use strict'; var Ti=null; //video标签变量 var videoEle=document.getElementsByTagName('video'),_videoEle=[],videoPlayer=null,videoNum=0; var oriHway='landscape-primary',oriHgamma=0,oriHbeta=0,isLock=true; function setVideo(){videoPlayer=this;videoOriLock();} function videoOriLock(){ if(videoPlayer.videoWidth>videoPlayer.videoHeight){isLock=true;} else{isLock=false;screen.orientation.unlock();} } //video标签事件绑定 function videoEvent(){ if(videoEle.length>videoNum){ //video事件初始化 if(!videoNum){ //重力感应 window.addEventListener('deviceorientation',function(e){ if(isLock){ oriHgamma=e.gamma; oriHbeta=(e.beta>0) ? e.beta : -e.beta; if((oriHbeta<65 || oriHbeta>115) && (oriHgamma<-25 || oriHgamma>25)){ oriHway=((oriHbeta<65 && oriHgamma<-25) || (oriHbeta>115 && oriHgamma>25)) ? 'landscape-primary' : 'landscape-secondary'; } screen.orientation.lock(oriHway); } }); } //播放video标签查找 for(Ti=videoNum;Ti0){ //Ajax重载video标签查找 for(Ti=0;Ti<_videoEle.length;Ti++){ if(!_videoEle[Ti].offsetWidth>0){ for(Ti=0;TistartX) ? endX-startX : startX-endX; angY=(endY>startY) ? endY-startY : startY-endY; if(angX>100 || angY>100){ if(angX>angY){direction=(endX>startX) ? '→' : '←';} else{direction=(endY>startY) ? '↓' : '↑';} if(path.charAt(path.length-1)!=direction){path+=direction;} startX=endX;startY=endY; } } }); //手指离开屏幕。 window.addEventListener('touchend', function(e){ nowTime=new Date().getTime(); //双击功能 if((nowTime-clickTime)<200){//双击判定 if((nowTime-saveTime)<400){ if(!reg.test(saveWords)){ saveWords='https://www.baidu.com/s?wd='+saveWords; }else if(saveWords.indexOf('http')<0){ saveWords='//'+saveWords; } GM_openInTab(saveWords,{active:true}); } if(document.webkitFullscreenElement){document.webkitExitFullscreen();} else if(videoPlayer){videoPlayer.webkitRequestFullScreen();} }else if(path.length<1 && (nowTime-touchTime)<200){//点击判定 clickTime=nowTime; } //滑动功能 if(videoPlayer && path.length<2){ //视频滑动 videoRect=videoPlayer.getBoundingClientRect(); if(_startX>videoRect.x && _startX<(videoRect.x+videoRect.width) && _startY>videoRect.y && _startY<(videoRect.y+videoRect.height)){ endX=e.changedTouches[0].clientX; angX=endX-_startX; if(angX>30 || angX<-30){ videoPlayer.currentTime+=angX*angX*angX/(25000*(1-(angX*angX/(40000+angX*angX)))); } } }else if(gesture[path]){ //手势执行 try{eval(pathFn[gesture[path]]);} catch(error){alert('“'+path+'” 手势执行脚本错误:\n'+error+' !');} } videoEvent(); path=''; }); } //手势功能原始数据 var gesture={ '↑→↓←':'打开设置', '→←':'后退', '←→':'前进', '↓↑':'回到顶部', '↑↓':'回到底部', '←↓':'刷新页面', '←↑':'新建页面', '→↓':'关闭页面', '→↑':'恢复页面', '↑→↓':'关闭其他页面' }, pathFn={ '打开设置':'openSet()', '后退':'history.go(-1);if(document.referrer==""){GM_setValue("lastTab",location.href);window.close();}', '前进':'history.go(1)', '回到顶部':'document.documentElement.scrollTop=0', '回到底部':'document.documentElement.scrollTop=document.documentElement.scrollHeight', '刷新页面':'history.go(0)', '新建页面':'GM_openInTab("about:blank",{active:true})', '关闭页面':'GM_setValue("lastTab",location.href);window.close()', '恢复页面':'GM_openInTab(GM_getValue("lastTab"),{active:true})', '关闭其他页面':'GM_setValue("closeAll", Date())' }; //手势存储数据读取 gesture=GM_getValue('gesture',gesture); pathFn=GM_getValue('pathFn',pathFn); sliderbackListener();//事件绑定 //手势操作设置UI var gestureUL=null,gestureEle=null,pathEle=null,gestureName='',gesturePath=''; function openSet(){ //页面生成 GM_addStyle('html{font-size:62.5% !important}'+ '#gestureBox{background-color:#fff;width:100%;height:100%;position:fixed;padding:0;margin:0;top:0;left:0;overflow-y:auto;z-index:999998}'+ '#gestureBox *{font-family:"Microsoft YaHei";margin:0;padding:0;text-align:center}'+ '#gestureBox h1{width:60%;height:4rem;line-height:4rem;font-size:2rem;color:#0074d9;background-color:#dee6ef;margin:1rem auto;border-radius:4rem;box-shadow:.3rem .3rem 1rem #dfdfdf}'+ '#gestureBox #addGesture{width:4rem;height:4rem;margin:0 auto 1rem auto;line-height:4rem;background-color:#dee6ef;color:#032e58;font-size:3rem;border-radius:4rem;box-shadow:.1rem .1rem .5rem #dfdfdf}'+ '#gestureBox .gestureLi{height:5rem;line-height:5rem;margin-top:1rem;width:100%;border-bottom:.3rem dashed #dfdfdf}'+ '#gestureBox .gestureLi p{width:38%;height:4rem;line-height:4rem;font-size:2rem;border-left:0.6rem solid;margin-left:1%;color:#ffb400;background-color:#fff1cf;float:left}'+ '#gestureBox .gestureLi .gesturePath{float:left;width:40%;height:4rem;background-color:#f3f3f3;color:#000;font-size:2rem;line-height:4rem;box-shadow:.1rem .1rem .5rem #ccc9c9;border-radius:1rem;margin-left:3%}'+ '#gestureBox .gestureLi .delGesture{width:5rem;height:4rem;line-height:4rem;border-radius:4rem;float:right;margin-right:1%;font-size:2rem;color:#f00;text-decoration:line-through}'+ '#gestureBox #revisePath{background-color:rgba(0,0,0,.5);width:100%;height:100%;position:fixed;top:0;left:0;overflow:hidden;z-index:999999;display:none;color:#000}'+ '#gestureBox #revisePath span{width:5rem;height:5rem;font-size:5rem;line-height:5rem;position:absolute}'+ '#gestureBox #revisePath div{position:absolute;width:30%;height:3rem;line-height:3rem;font-size:3rem;bottom:15%}'+ '#gestureBox #revisePath p{position:absolute;top:15%;font-size:4rem;line-height:4rem;height:4rem;width:100%}'+ '#gestureBox #revisePath #path{top:40%;color:#ffee03;font-size:6rem}'+ '#gestureBox #editGesture{background-color:#fff;width:100%;height:100%;position:fixed;top:0;left:0;overflow:hidden;z-index:999999;display:none;color:#000}'+ '#gestureBox #editGesture p{font-size:3rem;text-align:left;margin-top:3rem;margin-left:3rem;width:100%;height:3rem;line-height:3rem}'+ '#gestureBox #editGesture #gestureName{margin-top:1rem;width:80%;height:4rem;line-height:4rem;font-size:2rem;color:#000;border:0.1rem solid #dadada;border-radius:1rem;text-align:left;padding:0 1rem}'+ '#gestureBox #editGesture #pathFn{width:80%;margin-top:1rem;height:40%;font-size:2rem;text-align:left;line-height:2.2rem;padding:1rem;border:0.1rem solid #dadada;border-radius:1rem}'+ '#gestureBox #editGesture div{width:10rem;height:5rem;font-size:3rem;line-height:5rem;display:inline-block;color:#fff;background-color:#2866bd;margin:3rem 1rem 0rem 1rem}'); Ti=document.createElement('div'); Ti.setAttribute('id','gestureBox'); document.body.appendChild(Ti); Ti.innerHTML='

手势轨迹设置

+
'+ '
'+ '

请滑动手指

'+ '
Clear
Cancle
'+ '

手势名称:

'+ '

手势路径脚本:

'+ '
保存
关闭
'; gestureUL=document.getElementById('gestureUL'); pathEle=document.getElementById('path'); init(); //路径修改事件 document.getElementById('revisePath').addEventListener('touchmove',function(e){ e.stopPropagation(); e.preventDefault(); if(e.changedTouches.length==1){ endX=e.changedTouches[0].screenX; endY=e.changedTouches[0].screenY; angX=(endX>startX) ? endX-startX : startX-endX; angY=(endY>startY) ? endY-startY : startY-endY; if(angX>100 || angY>100){ if(angX>angY){direction=(endX>startX) ? '→' : '←';} else{direction=(endY>startY) ? '↓' : '↑';} if(pathEle.innerHTML.charAt(pathEle.innerHTML.length-1)!=direction){pathEle.innerHTML+=direction;} startX=endX;startY=endY; } } }); //清除路径 document.getElementById('clearPath').addEventListener('click',function(){pathEle.innerHTML='';}); //修改路径 document.getElementById('cancleRevise').addEventListener('click',function(){ if(pathEle.innerHTML){ delete gesture[gesturePath]; gesture[pathEle.innerHTML]=gestureName; GM_setValue('gesture',gesture); init(); } document.getElementById('revisePath').style.display='none'; }); //.新建手势 document.getElementById('addGesture').addEventListener('click',function(){ document.getElementById('gestureName').value=''; document.getElementById('pathFn').value=''; gestureName=''; document.getElementById('editGesture').style.display='block'; }); //保存手势 document.getElementById('saveGesture').addEventListener('click',function(){ if(document.getElementById('gestureName').value){ if(gestureName){ delete pathFn[gestureName]; for(Ti in gesture){ if(gesture[Ti]==gestureName){ gesture[Ti]=document.getElementById('gestureName').value; break; } } } pathFn[document.getElementById('gestureName').value]=document.getElementById('pathFn').value; GM_setValue('pathFn',pathFn); GM_setValue('gesture',gesture); init(); document.getElementById('editGesture').style.display='none'; }else{ alert('请输入手势名称!'); } }); //关闭编辑 document.getElementById('closeEdit').addEventListener('click',function(){ document.getElementById('editGesture').style.display='none'; }); } function editGesture(){ gestureName=this.getAttribute('name'); document.getElementById('gestureName').value=gestureName; document.getElementById('pathFn').value=pathFn[gestureName]; document.getElementById('editGesture').style.display='block'; } function revisePath(){ gestureName=this.getAttribute('name'); gesturePath=this.innerHTML; pathEle.innerHTML=''; document.getElementById('revisePath').style.display='block'; } function delGesture(){ gestureName=this.getAttribute('name'); delete pathFn[gestureName]; for(Ti in gesture){ if(gesture[Ti]==gestureName){ delete gesture[Ti]; break; } } GM_setValue('pathFn',pathFn); GM_setValue('gesture',gesture); init(); } function init(){ gestureUL.innerHTML=''; for(Ti in pathFn){ gesturePath=''; for(gestureName in gesture){ if(gesture[gestureName]==Ti){ gesturePath=gestureName; break; } } gestureUL.innerHTML+='

'+Ti+'

'+gesturePath+'
删除
'; } //编辑手势 gestureEle=document.querySelectorAll('#gestureBox .gestureLi p'); for(Ti=0;Ti