location_on 首页 keyboard_arrow_right 纪录片坊 keyboard_arrow_right 正文

给想要一键解决的人:蘑菇影视在线观看的音量与亮度手势我这样做

纪录片坊 access_alarms2026-02-07 visibility52 text_decrease title text_increase

给想要一键解决的人:蘑菇影视在线观看的音量与亮度手势我这样做

给想要一键解决的人:蘑菇影视在线观看的音量与亮度手势我这样做

我平时看在线视频时最烦的就是在手机或平板上去找音量键、去系统里调亮度,尤其看长剧或电影时频繁操作极影响观感。于是我自己做了一个简单实用的解决方案:在网页播放器上加入左右滑动手势——右侧上下滑调音量,左侧上下滑调“亮度”(通过覆盖层调整画面亮度),并带可视提示、数值记忆和双击快进等常用功能。下面把我做法完整写出来,直接照着装就能用,不需要复杂设置。

为什么要用这个

  • 手势更直觉:观看时只需单手滑动屏幕边缘即可。
  • 无需系统级权限:通过网页层实现亮度效果(不是真正改系统亮度),兼容主流HTML5播放器。
  • 可自定义:音量步进、亮度范围、是否启用双击快进等都能改。
  • 安装简单:通过浏览器用户脚本管理器(Tampermonkey/Violentmonkey)即可使用。

功能一览

  • 右侧上下滑:音量增减(有百分比提示)
  • 左侧上下滑:画面亮度(用覆盖层模拟,百分比提示)
  • 单击:显示/隐藏播放器默认控件
  • 双击左/右区域:向后/向前跳转(可设置秒数)
  • 设置持久化:音量和亮度会保存在本域名下的 localStorage

安装前说明(按需选择)

  • 桌面浏览器:Chrome/Edge/Firefox + Tampermonkey/Violentmonkey。
  • 安卓手机:建议用支持扩展的浏览器(如Firefox Android 或 Kiwi Browser),部分浏览器对Userscript有限制。
  • iOS:Safari对Userscript支持有限,建议使用有脚本支持的第三方浏览器或在桌面端使用。
  • 该脚本基于常见的HTML5 video元素和常见播放器容器,若个别站点播放器结构极特殊,可能需微调选择器。

一步步安装(以 Tampermonkey 为例)

  1. 在浏览器里安装 Tampermonkey(或 Violentmonkey)。
  2. 新建一个用户脚本,复制下面整段代码到脚本编辑窗口,保存并启用。
  3. 打开蘑菇影视的播放页(或其他视频页)刷新,应该可以直接使用左右滑手势。

完整可用脚本(直接复制)

// ==UserScript==
// @name         网页视频手势:音量 & 亮度 & 快进
// @match        *://*/*
// @grant        none
// @version      1.0
// @author       我
// @description  右侧上下滑调音量,左侧上下滑调亮度,双击快进/后退,支持本域持久化
// ==/UserScript==

(function() {
  'use strict';

  // 配置项(可按需修改)
  const SEEK_SECONDS = 10;         // 双击快进/后退秒数
  const VOLUME_STEP = 0.05;        // 单次滑动对应音量变化(基本单位)
  const BRIGHTNESS_STEP = 0.05;    // 亮度变化步进
  const OVERLAY_OPACITY = 0.6;     // 亮度覆盖层最大暗度(0-1),越大画面越暗
  const ENABLE_DOUBLE_TAP = true;

  // 工具:创建提示浮层
  function createToast() {
    const t = document.createElement('div');
    t.style.position = 'fixed';
    t.style.zIndex = 999999;
    t.style.left = '50%';
    t.style.top = '10%';
    t.style.transform = 'translateX(-50%)';
    t.style.padding = '8px 12px';
    t.style.background = 'rgba(0,0,0,0.6)';
    t.style.color = '#fff';
    t.style.borderRadius = '6px';
    t.style.fontSize = '14px';
    t.style.pointerEvents = 'none';
    t.style.display = 'none';
    document.body.appendChild(t);
    return t;
  }
  const toast = createToast();

  function showToast(text, ms=900) {
    toast.textContent = text;
    toast.style.display = 'block';
    clearTimeout(toast._t);
    toast._t = setTimeout(()=> toast.style.display='none', ms);
  }

  // 给视频元素添加手势支持
  function attachToVideo(video) {
    if (!video) return;
    // 防止重复附加
    if (video._gestureAttached) return;
    video._gestureAttached = true;

    // 尝试找到合适的容器(用于覆盖亮度层和接收手势)
    let container = video.closest('video, .player, .video-player, .embed, .plyr__video-embed') || video.parentElement || document.body;

    // 亮度覆盖层
    let overlay = container.querySelector('.gm-brightness-overlay');
    if (!overlay) {
      overlay = document.createElement('div');
      overlay.className = 'gm-brightness-overlay';
      Object.assign(overlay.style, {
        position: 'absolute',
        left: 0, top: 0, right: 0, bottom: 0,
        background: '#000',
        pointerEvents: 'none',
        zIndex: 999990,
        opacity: 0,
        transition: 'opacity 120ms linear'
      });
      // 确保容器定位
      const cs = window.getComputedStyle(container);
      if (cs.position === 'static') container.style.position = 'relative';
      container.appendChild(overlay);
    }

    // 视图提示(在container内)
    let indicator = container.querySelector('.gm-indicator');
    if (!indicator) {
      indicator = document.createElement('div');
      indicator.className = 'gm-indicator';
      Object.assign(indicator.style, {
        position: 'absolute',
        zIndex: 999995,
        left: '50%',
        top: '50%',
        transform: 'translate(-50%,-50%)',
        background: 'rgba(0,0,0,0.6)',
        color: '#fff',
        padding: '8px 12px',
        borderRadius: '6px',
        fontSize: '14px',
        display: 'none',
        pointerEvents: 'none'
      });
      container.appendChild(indicator);
    }

    // 载入持久化设置
    const keyPrefix = location.hostname + '_gm_';
    let storedVolume = parseFloat(localStorage.getItem(keyPrefix + 'volume'));
    if (!isFinite(storedVolume)) storedVolume = video.volume || 0.7;
    let storedBrightness = parseFloat(localStorage.getItem(keyPrefix + 'brightness'));
    if (!isFinite(storedBrightness)) storedBrightness = 1; // 1 = 原始亮度

    video.volume = Math.max(0, Math.min(1, storedVolume));
    overlay.style.opacity = Math.max(0, Math.min(1, (1 - storedBrightness) * OVERLAY_OPACITY));

    function showIndicator(text) {
      indicator.textContent = text;
      indicator.style.display = 'block';
      clearTimeout(indicator._t);
      indicator._t = setTimeout(()=> indicator.style.display='none', 900);
    }

    // 手势状态
    let startY = 0, startX = 0, lastY = 0, lastX = 0;
    let activeSide = null; // 'left' or 'right'
    let moved = false;
    let touchId = null;
    let lastTapTime = 0;

    function onStart(e) {
      const touch = e.touches ? e.touches[0] : e;
      touchId = touch.identifier || null;
      startY = lastY = touch.clientY;
      startX = lastX = touch.clientX;
      moved = false;
      activeSide = (startX < window.innerWidth / 2) ? 'left' : 'right';
    }

    function onMove(e) {
      const touch = e.touches ? Array.from(e.touches).find(t => t.identifier === touchId) || e.touches[0] : e;
      if (!touch) return;
      const dy = lastY - touch.clientY;
      const dx = touch.clientX - lastX;
      if (Math.abs(dy) < 2 && Math.abs(dx) < 2) return;
      moved = true;
      lastY = touch.clientY;
      lastX = touch.clientX;

      if (activeSide === 'right') {
        // 调整音量:上滑增大
        const delta = dy / window.innerHeight; // 比例
        const change = delta * 2; // 放大灵敏度
        let newVol = Math.max(0, Math.min(1, video.volume + change));
        video.volume = newVol;
        localStorage.setItem(keyPrefix + 'volume', newVol);
        showIndicator('音量 ' + Math.round(newVol * 100) + '%');
      } else {
        // 左侧:调亮度(通过overlay透明度模拟)
        const delta = dy / window.innerHeight;
        let currentBright = 1 - (parseFloat(overlay.style.opacity || 0) / OVERLAY_OPACITY);
        let newBright = Math.max(0.1, Math.min(1.2, currentBright + delta));
        overlay.style.opacity = Math.max(0, Math.min(1, (1 - newBright) * OVERLAY_OPACITY));
        localStorage.setItem(keyPrefix + 'brightness', newBright);
        showIndicator('亮度 ' + Math.round(newBright * 100) + '%');
      }
      e.preventDefault && e.preventDefault();
    }

    function onEnd(e) {
      const now = Date.now();
      if (!moved) {
        // 处理单击与双击
        if (ENABLE_DOUBLE_TAP) {
          if (now - lastTapTime < 350) {
            // 双击
            const side = activeSide;
            if (side === 'right') {
              // 快进
              video.currentTime = Math.min(video.duration || Infinity, video.currentTime + SEEK_SECONDS);
              showToast('快进 ' + SEEK_SECONDS + 's', 700);
            } else {
              // 后退
              video.currentTime = Math.max(0, video.currentTime - SEEK_SECONDS);
              showToast('后退 ' + SEEK_SECONDS + 's', 700);
            }
            lastTapTime = 0;
          } else {
            // 单击:切换默认控件显示(若有)
            const controls = video.controls;
            if (controls !== undefined) {
              video.controls = !controls;
              showToast(video.controls ? '显示控件' : '隐藏控件', 700);
            } else {
              showToast('点击', 500);
            }
            lastTapTime = now;
          }
        } else {
          showToast('点击', 500);
        }
      }
      touchId = null;
      moved = false;
    }

    // 支持触摸与鼠标
    container.addEventListener('touchstart', onStart, {passive: true});
    container.addEventListener('touchmove', onMove, {passive: false});
    container.addEventListener('touchend', onEnd);
    container.addEventListener('mousedown', function(e) {
      onStart(e);
      const moveHandler = function(ev) { onMove(ev); };
      const upHandler = function(ev) { onEnd(ev); document.removeEventListener('mousemove', moveHandler); document.removeEventListener('mouseup', upHandler); };
      document.addEventListener('mousemove', moveHandler);
      document.addEventListener('mouseup', upHandler);
    });

    // 监听video变化(若页面切换了视频源)
    const observer = new MutationObserver(()=> {
      // nothing special here now, placeholder for extension
    });
    observer.observe(container, {childList: true, subtree: true});
  }

  // 自动查找页面上的video元素并附加
  function scanAndAttach() {
    const videos = document.querySelectorAll('video');
    videos.forEach(v => attachToVideo(v));
  }

  // 初次运行和动态添加处理
  scanAndAttach();
  const globalObserver = new MutationObserver(scanAndAttach);
  globalObserver.observe(document.body, {childList: true, subtree: true});
})();

常见问题与优化建议

  • 脚本在个别站点失效:可能是该站点用特殊嵌入或自定义播放器,需修改 container 查找逻辑或增加定位选择器。把 console.log 信息打开有助排错。
  • 亮度“非真实系统亮度”:因为网页无法直接更改设备亮度,我用覆盖层模拟视觉效果,足以在暗光环境下改善观感。若你想要真正改系统亮度,需要系统/应用级权限。
  • 手势灵敏度调节:可以修改脚本顶部的 VOLUMESTEP、BRIGHTNESSSTEP 或 change 计算方式来调整灵敏度。
  • 手机兼容性:部分内置浏览器禁用扩展或脚本,建议使用支持扩展的浏览器。

我自己的使用经验

  • 在安卓平板上看剧时,右侧调音量、左侧调亮度的组合非常顺手;双击快进也让我轻松跳过片头片尾。
  • 我把 SEEK_SECONDS 设置为 12 秒,不同人习惯不同,可以按需更改。
  • 脚本轻量,几乎不影响性能;除非页面频繁创建大量 video 元素,否则基本无感。

report_problem 举报
关于蘑菇视频app下载的夜间模式,我只说三句
« 上一篇 2026-02-07
蘑菇影视官网周末晚上:投屏别再乱试了
下一篇 » 2026-02-08