🏠 

手机视频脚本

原脚本为【俺的手机视频脚本】。方便自用新增了0~10倍速播放功能。


安装此脚本?
// ==UserScript==
// @name         手机视频脚本
// @description  原脚本为【俺的手机视频脚本】。方便自用新增了0~10倍速播放功能。
// @author       酷安:lying_flat,原脚本作者: shopkeeperV,原脚本链接https://greasyfork.org/scripts/456542/code
// @namespace    https://greasyfork.org/users/1304874
// @version      1.8.3-fork-0.1
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addValueChangeListener
// @grant        GM_registerMenuCommand
// @grant        window.onurlchange
// ==/UserScript==
/*jshint esversion: 8*/
/**
* @param {HTMLVideoElement} video
* @param {HTMLDivElement} container */
function addSpeedControl(video, container) {
'use strict';
const open = {
container: `margin: 0; padding: 0;
width: 40px;
border-radius: 3px;
position: absolute;
top: min(8.5%, 35px);
left: 10px;
z-index: 999999;
height: 70vh;
background-color: white;
white-space: collapse nowrap;
border-radius: 1px;`,
toggle: `margin: 0; padding: 0;
width: 100%;
height: 30px;
font-size: 15px;
text-align: center;
color: black;
display: block`,
range: `margin: 0; padding: 0;
width: 100%;
height: calc(70vh - 70px);
appearance: slider-vertical;
writing-mode: vertical-lr;
direction: rtl;
display: block`,
reset: `margin: 0 0 3px; padding: 0;
width: 40px;
height: 40px;
display: block`,
};
const close = {
container: `margin: 0; padding: 0;
width: 40px;
border-radius: 3px;
position: absolute;
top: min(8.5%, 35px);
left: 10px;
z-index: 999999;
max-height: 40px;
background-color: transparent;`,
toggle: `margin: 0; padding: 0;
width: 100%;
font-size: 15px;
text-align: center;
height: 40px;
color: white;
background-color: rgba(0, 0, 0, 0.25);
border-radius: 20px;
box-sizing: border-box;`,
range: `margin: 0; padding: 0;
display: none;`,
reset: `margin: 0; padding: 0;
display: none;`,
};
const svg =
'<svg xmlns="http://www.w3.org/2000/svg" width="40px" height="35px" viewBox="0 0 24 24"><path fill="currentColor" d="M4.038 12.006q0 2.47 1.4 4.488q1.399 2.018 3.704 2.906q.235.09.344.286t.004.372q-.123.202-.353.266q-.23.064-.45-.02q-2.575-1.004-4.122-3.275q-1.546-2.271-1.546-5.042q0-.785.136-1.554q.135-.77.42-1.51L1.827 9.931q-.177.11-.375.055t-.302-.232q-.104-.177-.049-.378q.055-.201.232-.305l2.775-1.596q.292-.162.618-.078t.487.376l1.597 2.75q.103.177.049.378q-.055.2-.232.305t-.378.049q-.2-.055-.305-.232L4.69 8.875q-.332.738-.492 1.53t-.16 1.6ZM12 4.019q-1.352 0-2.612.455T7.075 5.775q-.183.142-.403.128q-.22-.015-.324-.191q-.129-.208-.061-.438q.067-.23.255-.378q1.183-.92 2.565-1.398q1.381-.479 2.874-.479q1.956 0 3.7.786q1.746.785 3.036 2.262V3.981q0-.214.144-.357q.143-.143.356-.143q.214 0 .357.143t.143.357v3.192q0 .348-.23.578q-.23.23-.577.23h-3.193q-.213 0-.356-.143t-.144-.357q0-.214.144-.357q.143-.143.356-.143h2.475q-1.13-1.425-2.755-2.193q-1.625-.769-3.437-.769Zm5.973 13.256q1.22-1.388 1.69-3.137q.472-1.748.125-3.544q-.038-.232.086-.423q.124-.19.338-.19q.251 0 .414.19q.162.19.2.442q.328 1.895-.175 3.744q-.503 1.849-1.747 3.341q-.994 1.204-2.332 1.994q-1.337.79-2.874 1.087l1.73.996q.178.104.23.305q.052.2-.052.378t-.302.229q-.198.051-.375-.052l-2.762-1.616q-.292-.161-.376-.487q-.083-.326.078-.619l1.596-2.736q.104-.177.302-.229t.375.052q.177.104.232.305q.055.2-.049.378l-1.31 2.229q1.445-.2 2.726-.869t2.232-1.768Z" /></svg>';
const html = `<button class="toggle" type="button">×1</button>
<input class="range" type="range" name="speed" value="10" step="1" min="0" max="100" defaultValue="10" orient="vertical" />
<button class="reset" type="button">${svg}</button>`;
/**
* @param {HTMLVideoElement} video
* @param {HTMLDivElement} container */
addSpeedControl = (video, container) => {
container.classList.add('close');
container.insertAdjacentHTML('beforeend', html);
// 获取元素
const toggleButton = container.querySelector('.toggle');
const rangeInput = container.querySelector('.range');
const resetButton = container.querySelector('.reset');
container.style.cssText = close.container;
toggleButton.style.cssText = close.toggle;
rangeInput.style.cssText = close.range;
resetButton.style.cssText = close.reset;
// 设置播放速率
function updatePlaybackRate(event) {
const value = parseInt(rangeInput.value);
video.playbackRate = value / 10;
toggleButton.innerHTML = valueToString(value);
event.stopPropagation();
}
// 重置播放速率
function resetPlaybackRate(event) {
video.playbackRate = 1;
toggleButton.innerHTML = '×1';
rangeInput.value = 10;
event.stopPropagation();
}
// 绑定事件
toggleButton.addEventListener('click', (event) => {
container.classList.toggle('close');
const css = container.classList.contains('close') ? open : close;
container.style.cssText = css.container;
toggleButton.style.cssText = css.toggle;
rangeInput.style.cssText = css.range;
resetButton.style.cssText = css.reset;
event.stopPropagation();
});
rangeInput.addEventListener('input', updatePlaybackRate);
rangeInput.addEventListener('change', updatePlaybackRate);
resetButton.addEventListener('click', resetPlaybackRate);
};
function valueToString(v) {
if (v == 0) return '&#9654;';
const r = v % 10;
if (r == 0) return `×${v / 10}`;
return `×${(v - r) / 10}.${r}`;
}
//first call
addSpeedControl(video, container);
}
(function () {
'use strict';
//去除未使用框架的视频的原生全屏按钮
let videos = document.getElementsByTagName('video');
for (let video of videos) {
if (video.controls) {
video.controlsList = ['nofullscreen'];
}
}
//放开iframe全屏
let iframes = document.getElementsByTagName('iframe');
for (let iframe of iframes) {
iframe.allowFullscreen = true;
}
//部分网站阻止视频操作层触摸事件传播,需要指定监听目标,默认是document
//注意,对少数iframe内视频,广告插件或使此脚本不起作用
let listenTarget = document;
//youtube使用无刷新网页,需要监听地址变化重新监听操控层
if (window.location.host === 'm.youtube.com') {
let refresh = function () {
console.log('俺的手机视频脚本:页面刷新...');
//youtube视频在脚本执行时还没加载,需要个定时器循环获取状态
if (window.location.href.search('watch') >= 0) {
let waitForVideo = function () {
console.log('俺的手机视频脚本:正在获取视频...');
//特定的视频操控层
let videos = document.getElementsByTagName('video');
let listenTargetArray = document.getElementsByClassName('player-controls-background');
if (videos.length > 0) {
let video = videos[0];
//非静音播放中
if (video.readyState > 1 && !video.paused && !video.muted) {
listenTarget = listenTargetArray[0];
//防止重复添加
if (listenTarget.getAttribute('me_video_js')) {
return;
}
listenTarget.setAttribute('me_video_js', 'me_video_js');
//视频已加载
console.log('俺的手机视频脚本:开始监听手势。');
listen();
return;
}
}
setTimeout(waitForVideo, 500);
};
waitForVideo();
}
};
refresh();
//考虑到有chrome和tampermonkey以外的用户,也适配不支持window.onurlchange的浏览器
if (/xmonkey|tampermonkey/i.test(GM_info.scriptHandler)) {
window.addEventListener('urlchange', refresh);
} else {
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;
history.pushState = function (state) {
originalPushState.apply(history, arguments);
console.log('监听到地址变化。pushState()调用。');
//太快的话原视频还没移除,会判断操控层已监听,不再等待新视频加载
setTimeout(refresh, 500);
};
history.replaceState = function (state) {
originalReplaceState.apply(history, arguments);
console.log('监听到地址变化,replaceState()调用。');
setTimeout(refresh, 500);
};
}
}
//通用
listen();
if (window === top) {
if (GM_getValue('voiced') == null) {
GM_setValue('voiced', true);
}
if (GM_getValue('speed') == null) {
GM_setValue('speed', true);
}
let diyConfig = function (configName, key) {
GM_registerMenuCommand(configName, () => {
let value = GM_getValue(key);
GM_setValue(key, !value);
alert(`成功设置为${!value},页面将刷新。`);
window.location.reload();
});
};
diyConfig('启用/关闭【触摸视频时取消静音】', 'voiced');
diyConfig('显示/隐藏【播放速度调整按钮】', 'speed');
}
function listen() {
if (listenTarget.tagName /*监听的是元素*/) {
//做个标记方便调试
listenTarget.setAttribute('listen_mark', true);
}
//对视频的查找与控制都是在每次touchstart后重新执行的
//虽然这样更消耗性能,但是对不同的网站兼容性更强
listenTarget.addEventListener('touchstart', (e) => {
//为了代码逻辑在普通视频与iframe内视频的通用性,分别使用了clientX和screenY
let startX;
let startY;
let endX;
let endY;
//多根手指不做响应
if (e.touches.length === 1) {
//在全屏时,不对边缘5%的区域做响应
let screenX = e.touches[0].screenX;
let screenY = e.touches[0].screenY;
if (document.fullscreenElement) {
if (screenX < screen.width * 0.05 || screenX > screen.width * 0.95 || screenY < screen.height * 0.05 || screenY > screen.height * 0.95) return;
}
//单指触摸,记录位置
startX = Math.ceil(e.touches[0].clientX);
startY = Math.ceil(screenY);
endX = startX;
endY = startY;
} else return;
let videoElement;
//触摸的目标如果是视频或视频操控层,那他也是我们绑定手势的目标
let target = e.target;
//用于有操控层的网站,保存的是视频与操控层适当尺寸下的最大共同祖先节点,确认后需要在后代内搜索视频元素
let biggestContainer;
let targetWidth = target.clientWidth;
let targetHeight = target.clientHeight;
//所有大小合适的祖先节点最后一个为biggestContainer
let suitParents = [];
//用于判断是否含有包裹视频的a标签,需要禁止其被长按时呼出浏览器菜单
let allParents = [];
let temp = target;
//用于抖音类网站,滚动高度超过阈值后,不再继续查找祖先
let findAllSuitParent = false;
//抖音类短视频网站,特点是视频操控层占据几乎整个屏幕
let maybeTiktok = false;
//用于短视频判断
let scrollHeightOut = false;
//寻找biggestContainer
while (true) {
temp = temp.parentElement;
if (!temp /*或直接点击到html元素,他将没有父元素*/) {
return;
}
//allParents全部保存,用于判断是否存在a标签
allParents.push(temp);
if (!findAllSuitParent && temp.clientWidth > 0 && temp.clientWidth < targetWidth * 1.2 && temp.clientHeight > 0 && temp.clientHeight < targetHeight * 1.2) {
//用非全屏状态下scrollHeight来判断可以准确找到抖音类网站的合适视频容器
if (document.fullscreenElement) {
//全屏时视觉尺寸合适都可以用,youtube全屏就有滚动高度超出限制的元素
//suitParents保存适合的尺寸的祖先节点
suitParents.push(temp);
} else {
//非全屏时要判断一下滚动高度
if (temp.scrollHeight < targetHeight * 1.2) {
suitParents.push(temp);
} else {
findAllSuitParent = true;
scrollHeightOut = true;
}
}
}
//循环结束条件
if (temp.tagName === 'BODY' || temp.tagName === 'HTML' || !temp.parentElement) {
//已找到所有符合条件的祖先节点,取最后一个
if (suitParents.length > 0) {
biggestContainer = suitParents[suitParents.length - 1];
}
//没有任何大小合适的祖先元素,且自身不是视频元素,那也肯定不是视频操控层
else if (target.tagName !== 'VIDEO') {
return;
}
//gc
suitParents = null;
break;
}
}
//寻找视频元素
//当触摸的不是视频元素,可能是非视频相关组件,或视频的操控层
if (target.tagName !== 'VIDEO') {
//尝试获取视频元素
let videoArray = biggestContainer.getElementsByTagName('video');
if (videoArray.length > 0) {
videoElement = videoArray[0];
//找到视频元素后,可以判断是否可能是短视频
//非全屏状态下,非iframe内视频,若视频操作层或视频占据大半的屏幕,判断为短视频
//tiktok没有视频控件,判断这个防止有页面的预览视频铺满了屏幕,这一项只能判断到没有框架的视频
if (!document.fullscreenElement && top === window && !videoElement.controls && scrollHeightOut && target.clientHeight > window.innerHeight * 0.8) {
maybeTiktok = true;
}
//如果是视频外很大的容器绝非我们想要的
//操作层除了短视频没见过高度高视频这么多的,大概率不是视频操控层
if (!maybeTiktok && targetHeight > videoElement.clientHeight * 1.5) {
//不是合适的操作层
return;
}
if (videoArray.length > 1) {
console.log('触摸位置找到不止一个视频。');
}
} else {
//非视频相关组件
return;
}
}
//触摸的是视频元素,则一切清晰明了
else {
videoElement = target;
}
//用于比较单击后,视频的播放状态,如果单击暂停,则恢复播放
let playing = !videoElement.paused;
//下面两个连通tiktok变量3个参数用于判断是否要执行touchmove事件处理器
//小于30s当做预览视频,在网页上的视频列表可能存在,不要让他们影响网页滚动
let sampleVideo = false;
let videoReady = false;
let videoReadyHandler = function () {
videoReady = true;
if (videoElement.duration < 30) {
sampleVideo = true;
}
};
if (videoElement.readyState > 0) {
videoReadyHandler();
} else {
videoElement.addEventListener('loadedmetadata', videoReadyHandler, { once: true });
}
//一个合适尺寸的最近祖先元素用于显示手势信息与全屏按钮
let componentContainer = findComponentContainer();
//指示器元素
let notice;
//视频快进快退量
let timeChange = 0;
//1表示右滑快进,2表示左滑快退,方向一旦确认就无法更改
let direction;
//优化a标签导致的长按手势中断问题(许多网站的视频列表的预览视频都是由a标签包裹)
makeTagAQuiet();
//禁止长按视频呼出浏览器菜单,为长按倍速做准备(没有视频框架的视频需要)
if (!videoElement.getAttribute('disable_contextmenu') /*只添加一次监听器*/) {
videoElement.addEventListener('contextmenu', (e) => {
e.preventDefault();
});
videoElement.setAttribute('disable_contextmenu', true);
}
//禁止图片长按呼出浏览器菜单和拖动(部分框架视频未播放时,触摸到的是预览图,抖音类播放时摸到的都是图片)
if (target.tagName === 'IMG') {
target.draggable = false;
if (!target.getAttribute('disable_contextmenu')) {
target.addEventListener('contextmenu', (e) => {
e.preventDefault();
});
target.setAttribute('disable_contextmenu', true);
}
}
let sharedCSS =
'border-radius:4px;z-index:99999;opacity:0.5;background-color:black;color:white;' + 'display:flex;justify-content:center;align-items:center;text-align:center;user-select:none;';
let haveControls = videoElement.controls;
let longPress = false;
//长按倍速定时器
let rateTimer = setTimeout(() => {
videoElement.playbackRate = 4;
videoElement.controls = false;
//禁止再快进快退
target.removeEventListener('touchmove', touchmoveHandler);
//显示notice
notice.innerText = 'x4';
notice.style.display = 'flex';
longPress = true;
rateTimer = null;
//显示调速按钮
//仅在全屏时触发
if (!document.fullscreenElement || videoElement.readyState === 0 || !GM_getValue('speed')) {
return;
}
{
let containers = componentContainer.getElementsByClassName('me-speed-container');
let container;
if (containers.length > 0) {
container = containers[0];
container.style.display = 'block';
} else {
container = document.createElement('div');
//在一次全屏状态中不会重复创建容器
container.className = 'me-speed-container';
try {
addSpeedControl(videoElement, container);
} catch (error) {
alert(`addSpeedControl函数运行出错:${error}`);
}
componentContainer.appendChild(container);
}
//触摸空白处关闭container
target.addEventListener(
'touchstart',
() => {
container.style.display = 'none';
},
{ once: true },
);
window.addEventListener(
'resize',
() => {
container.style.display = 'none';
},
{ once: true },
);
}
}, 800);
//有些网站预览视频位置实际在屏幕之外,需要加上平移的数值
let screenWidth = screen.width;
let componentMoveLeft = componentContainer.offsetLeft;
let moveNum = Math.floor((componentMoveLeft * 1.1) / screenWidth);
//添加指示器元素
let notices = componentContainer.getElementsByClassName('me-notice');
if (notices.length === 0) {
notice = document.createElement('div');
notice.className = 'me-notice';
let noticeWidth = 110; //未带单位,后面需要加单位
let noticeTop = Math.round(componentContainer.clientHeight / 6);
let noticeLeft = Math.round(moveNum * screenWidth + componentContainer.clientWidth / 2 - noticeWidth / 2);
notice.style.cssText = sharedCSS + 'font-size:16px;position:absolute;display:none;letter-spacing:normal;';
notice.style.width = noticeWidth + 'px';
notice.style.height = '30px';
notice.style.left = noticeLeft + 'px';
notice.style.top = noticeTop + 'px';
componentContainer.appendChild(notice);
//每次全屏与退出全屏需要重新计算notice的位置
window.addEventListener(
'resize',
() => {
notice.remove();
},
{ once: true },
);
} else {
notice = notices[0];
}
//滑动流畅的关键1,passive为false代表处理器内调用preventDefault()不会被浏览器拒绝
//mdn:文档级节点 Window、Document 和 Document.body默认是true,其他节点默认是false
target.addEventListener('touchmove', touchmoveHandler /*, {passive: false}*/);
target.addEventListener('touchend', touchendHandler, { once: true });
function makeTagAQuiet() {
for (let element of allParents) {
if (element.tagName === 'A' && !element.getAttribute('disable_menu_and_drag')) {
//禁止长按菜单
element.addEventListener('contextmenu', (e) => {
e.preventDefault();
});
//禁止长按拖动
element.draggable = false;
element.setAttribute('disable_menu_and_drag', true);
//没有长按菜单,用target="_blank"属性来平替
element.target = '_blank';
//不可能a标签嵌套a标签吧
break;
}
}
allParents = null;
}
function findComponentContainer() {
let temp = videoElement;
while (true) {
//寻找最近的有长宽数值的祖先节点
if (temp.parentElement.clientWidth > 0 && temp.parentElement.clientHeight > 0) {
return temp.parentElement;
} else {
temp = temp.parentElement;
}
}
}
function getClearTimeChange(timeChange) {
timeChange = Math.abs(timeChange);
let minute = Math.floor(timeChange / 60);
let second = timeChange % 60;
return (minute === 0 ? '' : minute + 'min') + second + 's';
}
function touchmoveHandler(moveEvent) {
console.log('手指移动');
//触摸屏幕后,0.8s内如果有移动,清除长按定时事件
if (rateTimer) {
clearTimeout(rateTimer);
rateTimer = null;
}
if (maybeTiktok || sampleVideo || !videoReady) {
return;
}
//滑动流畅的关键2
moveEvent.preventDefault();
if (moveEvent.touches.length === 1) {
//仅支持单指触摸,记录位置
let temp = Math.ceil(moveEvent.touches[0].clientX);
//x轴没变化,y轴方向移动也会触发,要避免不必要的运算
if (temp === endX) {
return;
} else {
endX = temp;
}
endY = Math.ceil(moveEvent.touches[0].screenY);
//console.log("移动到" + endX + "," + endY);
}
//由第一次移动确认手势方向,就不再变更
//10个像素起
if (endX > startX + 10) {
//快进
if (!direction) {
//首次移动,记录方向
direction = 1;
}
if (direction === 1) {
//方向未变化
timeChange = endX - startX - 10;
} else {
timeChange = 0;
}
} else if (endX < startX - 10) {
//快退
if (!direction) {
//首次移动,记录方向
direction = 2;
}
if (direction === 2) {
//方向未变化
timeChange = endX - startX + 10;
} else {
timeChange = 0;
}
} else if (timeChange !== 0) {
timeChange = 0;
} else {
return;
}
if (notice.style.display === 'none' /*已经显示了就不管怎么滑动了*/ && Math.abs(endY - startY) > Math.abs(endX - startX)) {
//垂直滑动不显示
timeChange = 0;
return;
}
//未到阈值不显示
if (direction) {
notice.style.display = 'flex';
notice.innerText = (direction === 1 ? '>>>' : '<<<') + getClearTimeChange(timeChange);
}
}
function touchendHandler() {
if (notice) notice.style.display = 'none';
// console.log("手指抬起");
if (GM_getValue('voiced')) {
videoElement.muted = false;
}
//所有非短视频自带的全视频区域的单击暂停,给他重新播放,手机不适合单击暂停,需要暂停的使用暂停按钮即可
//带延迟是为了让网页自带的js先执行,videoElement.paused的状态才会判断准确
setTimeout(() => {
if (playing && videoElement.paused && !maybeTiktok) {
videoElement.play();
}
}, 200);
//一般有chrome自带视频控件的就是没用框架的视频
//需要替换全屏按钮,不然无法显示快进指示器
//非长按后手指抬起时才添加全屏按钮
if (!longPress && videoElement.controls && !document.fullscreenElement) {
let btns = componentContainer.getElementsByClassName('me-fullscreen-btn');
let btn;
if (btns.length === 0) {
btn = document.createElement('div');
btn.style.cssText =
sharedCSS + 'position:absolute;width:40px;padding:2px;font-size:14px;font-weight:bold;' + 'box-sizing:border-box;border:1px solid white;white-space:normal;';
btn.innerText = '点我\n全屏';
//设置id是为了防止多次点击重复添加
btn.className = 'me-fullscreen-btn';
let divHeight = 40;
btn.style.height = divHeight + 'px';
btn.style.top = Math.round(componentContainer.clientHeight / 2 - divHeight / 2 - 10) + 'px';
btn.style.left = Math.round(moveNum * screenWidth + (componentContainer.clientWidth * 5) / 7) + 'px';
componentContainer.append(btn);
btn.addEventListener('touchstart', async function () {
btn.style.display = 'none';
await componentContainer.requestFullscreen();
});
//屏蔽原生全屏按钮
videoElement.controlsList = ['nofullscreen'];
} else {
btn = btns[0];
btn.style.display = 'flex';
}
setTimeout(() => {
btn.style.display = 'none';
}, 2000);
}
//滑动长按判断
if (endX === startX) {
//长按
//console.log("长按");
if (rateTimer) {
//定时器也许已经执行,此时清除也没关系
clearTimeout(rateTimer);
}
if (longPress) {
//长按快进结束如果原本有控制器,则恢复
videoElement.controls = haveControls;
videoElement.playbackRate = 1;
}
} else {
if (timeChange !== 0) {
//快进
videoElement.currentTime += timeChange;
}
//console.log("x轴移动" + (endX - startX));
//console.log("y轴移动" + (endY - startY));
}
target.removeEventListener('touchmove', touchmoveHandler);
}
});
}
//全屏横屏模块
//将浏览器锁定方向的方法改掉,防止网页自带的js执行,当此脚本执行时又把他改回来
//这是因为遇到有网站锁定为any后,且后于此脚本执行,那么手机倒着拿就会直接退出全屏
window.tempLock = screen.orientation.lock;
let myLock = function () {
console.log('网页自带js试图执行lock()');
};
screen.orientation.lock = myLock;
//顶层窗口负责执行横屏,因为iframe可能开启了沙箱机制无法锁定方向并无法修改
//使用油猴的变量监听,绕开iframe跨域限制
if (top === window) {
GM_setValue('doLock', false);
GM_addValueChangeListener('doLock', async function (key, oldValue, newValue) {
if (document.fullscreenElement && newValue) {
//恢复lock()
screen.orientation.lock = window.tempLock;
await screen.orientation.lock('landscape');
//变向结束再次修改lock()
screen.orientation.lock = myLock;
GM_setValue('doLock', false);
}
});
}
//全屏后触发resize次数,如果有iframe,每个document可不是共用这个值
let inTimes = 0;
//利用window的resize事件监听全屏动作,监听document常用的fullscreenchange事件可能因为后代停止传播而捕获不到
window.addEventListener('resize', () => {
//resize事件或先于全屏事件触发,此时判断是否全屏将出错,所以得设置延迟
setTimeout(fullscreenHandler, 500);
});
function fullscreenHandler() {
//获取全屏元素,查找视频,判断视频长宽比来锁定方向
let _fullscreenElement = document.fullscreenElement;
if (_fullscreenElement) {
//如果全屏元素是iframe,说明不是视频所在的document执行到这,记录也没用
if (_fullscreenElement.tagName === 'IFRAME') {
return;
}
//inTimes==1可代表全屏
inTimes++;
} else if (inTimes > 0) {
//此代码块可代表退出全屏
inTimes = 0;
} else {
//退出全屏时多余的触发或者是其他与全屏无关的元素触发resize
return;
}
if (inTimes !== 1) {
return;
}
let videoElement;
if (_fullscreenElement.tagName !== 'VIDEO') {
//最大的全屏元素不是视频本身,需要寻找视频元素
let videoArray = _fullscreenElement.getElementsByTagName('video');
if (videoArray.length > 0) {
videoElement = videoArray[0];
if (videoArray.length > 1) {
console.log('全屏元素内找到不止一个视频。');
}
}
} else videoElement = _fullscreenElement;
//也可能不是视频在全屏
if (videoElement) {
let changeHandler = function () {
//高度小于宽度,需要转向,landscape会自动调用陀螺仪
if (videoElement.videoHeight < videoElement.videoWidth) {
//开启沙盒机制的iframe修改sandbox属性无效,需要顶层窗口调用方向锁定
GM_setValue('doLock', true);
}
};
//视频未加载,在加载后再判断需不需要转向
if (videoElement.readyState < 1) {
videoElement.addEventListener('loadedmetadata', changeHandler, { once: true });
} else {
changeHandler();
}
}
}
})();