Greasy Fork is available in English.
双击切换全屏,不影响视频操作,键盘弹出时对话框自动抬高。就这么一个简单的操作,居然没人能做好,那就自己来!
// ==UserScript==
// @name 双击全屏(使用手机浏览器)
// @namespace http://tampermonkey.net/
// @version 1.7
// @description 双击切换全屏,不影响视频操作,键盘弹出时对话框自动抬高。就这么一个简单的操作,居然没人能做好,那就自己来!
// @author 青野
// @match *://*/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
let lastTapTime = 0;
const doubleTapDelay = 300; // 双击间隔时间(毫秒)
// 检查是否为可输入元素
function isInputElement(element) {
const tagName = element.tagName.toLowerCase();
const inputTags = ['input', 'textarea', 'select'];
const isContentEditable = element.isContentEditable;
const isInputRole = element.getAttribute('role') === 'textbox';
return inputTags.includes(tagName) || isContentEditable || isInputRole;
}
// 检查是否在视频控件区域
function isVideoControls(element) {
let current = element;
while (current) {
if (current.tagName && current.tagName.toLowerCase() === 'video') {
return false; // 视频元素本身可以双击
}
// 检查是否是视频控件容器
const className = current.className || '';
if (typeof className === 'string' &&
(className.includes('controls') ||
className.includes('player-controls') ||
className.includes('video-controls'))) {
return true;
}
current = current.parentElement;
}
return false;
}
// 检查当前是否处于全屏状态
function isFullscreen() {
return !!(document.fullscreenElement ||
document.webkitFullscreenElement ||
document.mozFullScreenElement ||
document.msFullscreenElement);
}
// 进入全屏
function enterFullscreen() {
const elem = document.documentElement;
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen();
} else if (elem.mozRequestFullScreen) {
elem.mozRequestFullScreen();
} else if (elem.msRequestFullscreen) {
elem.msRequestFullscreen();
}
}
// 退出全屏
function exitFullscreen() {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
// 切换全屏
function toggleFullscreen() {
if (!isFullscreen()) {
enterFullscreen();
} else {
exitFullscreen();
}
}
// 双击事件处理
function handleTap(e) {
const target = e.target;
// 忽略输入元素
if (isInputElement(target)) {
return;
}
// 忽略视频控件区域
if (isVideoControls(target)) {
return;
}
const currentTime = new Date().getTime();
const tapLength = currentTime - lastTapTime;
if (tapLength < doubleTapDelay && tapLength > 0) {
// 双击检测成功
e.preventDefault();
toggleFullscreenWithFlag();
lastTapTime = 0; // 重置
} else {
lastTapTime = currentTime;
}
}
// 监听触摸结束事件(移动端)
document.addEventListener('touchend', function(e) {
// 确保只是单点触摸
if (e.changedTouches.length === 1) {
handleTap(e);
}
}, { passive: false });
// 处理返回键:在全屏状态下,保持全屏并执行返回上一页
// 核心思路:检测非双击导致的全屏退出,自动重新进入全屏
let isDoubleTapExit = false; // 标记是否是双击主动退出全屏
// 重写 toggleFullscreen,标记双击退出
function toggleFullscreenWithFlag() {
if (!isFullscreen()) {
enterFullscreen();
} else {
isDoubleTapExit = true;
exitFullscreen();
}
}
// 监听全屏状态变化
function handleFullscreenChange() {
if (!isFullscreen()) {
// 全屏被退出了
if (isDoubleTapExit) {
// 是用户双击主动退出的,不做处理
isDoubleTapExit = false;
} else {
// 不是双击退出的(比如按了返回键),重新进入全屏
// 使用 requestAnimationFrame 确保在下一帧执行
requestAnimationFrame(() => {
enterFullscreen();
});
}
}
}
document.addEventListener('fullscreenchange', handleFullscreenChange);
document.addEventListener('webkitfullscreenchange', handleFullscreenChange);
document.addEventListener('mozfullscreenchange', handleFullscreenChange);
document.addEventListener('MSFullscreenChange', handleFullscreenChange);
// 键盘弹出时对话框抬高处理
function handleKeyboardResize() {
// 检测可能的对话框元素
const dialogSelectors = [
'dialog',
'[role="dialog"]',
'[role="alertdialog"]',
'.modal',
'.dialog',
'.popup',
'[class*="modal"]',
'[class*="dialog"]',
'[class*="popup"]'
];
const visualViewport = window.visualViewport;
if (visualViewport) {
visualViewport.addEventListener('resize', function() {
const keyboardHeight = window.innerHeight - visualViewport.height;
dialogSelectors.forEach(selector => {
const dialogs = document.querySelectorAll(selector);
dialogs.forEach(dialog => {
if (dialog.offsetParent !== null) { // 可见的对话框
if (keyboardHeight > 100) { // 键盘可能已弹出
dialog.style.transition = 'transform 0.3s ease';
dialog.style.transform = `translateY(-${keyboardHeight / 2}px)`;
} else {
dialog.style.transform = 'translateY(0)';
}
}
});
});
});
visualViewport.addEventListener('scroll', function() {
// 保持对话框位置
const scrollTop = visualViewport.offsetTop;
dialogSelectors.forEach(selector => {
const dialogs = document.querySelectorAll(selector);
dialogs.forEach(dialog => {
if (dialog.offsetParent !== null &&
dialog.style.position === 'fixed') {
dialog.style.top = `${scrollTop}px`;
}
});
});
});
}
// 备用方案:监听焦点事件
document.addEventListener('focusin', function(e) {
if (isInputElement(e.target)) {
setTimeout(() => {
dialogSelectors.forEach(selector => {
const dialogs = document.querySelectorAll(selector);
dialogs.forEach(dialog => {
if (dialog.offsetParent !== null) {
dialog.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
});
});
}, 300);
}
});
document.addEventListener('focusout', function(e) {
if (isInputElement(e.target)) {
setTimeout(() => {
dialogSelectors.forEach(selector => {
const dialogs = document.querySelectorAll(selector);
dialogs.forEach(dialog => {
dialog.style.transform = 'translateY(0)';
});
});
}, 100);
}
});
}
// 初始化键盘处理
handleKeyboardResize();
console.log('🐵 双击全屏脚本已加载 v1.2 - 作者:青野');
})();