Greasy Fork

Greasy Fork is available in English.

双击全屏(使用手机浏览器)

双击切换全屏,不影响视频操作,键盘弹出时对话框自动抬高。就这么一个简单的操作,居然没人能做好,那就自己来!

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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 - 作者:青野');
})();