You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使自定义Video Controls在视频全屏模式下始终可见

解决自定义视频控件全屏时隐藏的问题

核心问题原因

当前代码仅让<video>元素单独进入全屏,而自定义控件是位于<video>外部的独立<div>,全屏模式下浏览器只会渲染全屏的<video>元素,导致控件被排除在全屏视图外。

解决方案步骤

1. 调整HTML结构,将视频与控件放入同一容器

把视频和控件包裹在一个容器内,确保全屏时两者同时进入全屏状态:

<div class="video-container position-relative">
  <video id="video" class="video w-100" playsinline>
    <source src="https://test.loro.ec/www/t/test.loro.ec/images/articles_blocks/608.mp4" type="video/mp4">
  </video>
  <div class="controls position-absolute bottom-0 w-100 p-3">
    <div class="row align-items-center">
      <div class="col-1">
        <button class="controls__button toggleButton btn" id="toggleButton" title="Toggle Play" onclick="togglePlay()">►</button>
      </div>
      <div class="progress col-5 p-0" id="progressId">
        <div class="progress__filled" style="background: rebeccapurple;"></div>
      </div>
      <div class="col-4 d-flex align-items-center gap-2">
        <i class="bi bi-volume-up"></i> 
        <input class="form-range flex-grow-1" type="range" id="volume-bar" title="volume" min="0" max="1" step="0.1" value="1">
      </div>
      <div class="col-1">
        <button class="btn" onclick="toggleFullScreen()"><i class="bi bi-arrows-fullscreen"></i></button>
      </div>
    </div>
  </div>
</div>
  • 新增video-container容器作为控件的定位基准
  • 将控件改为绝对定位,固定在视频底部并铺满宽度
  • 使用Bootstrap布局类优化控件内部排版

2. 隐藏浏览器默认视频控件

添加CSS规则强制隐藏浏览器自带控件,避免与自定义控件冲突:

video::-webkit-media-controls {
  display: none !important;
}
video::-moz-media-controls {
  display: none !important;
}
video::-ms-media-controls {
  display: none !important;
}
video {
  controls: none !important;
}

3. 修改全屏切换函数,针对容器而非视频

更新toggleFullScreen函数,让整个容器进入全屏:

const videoContainer = document.querySelector('.video-container');

function toggleFullScreen() {
  if (videoContainer.requestFullscreen) {
      document.fullscreenElement ? document.exitFullscreen() : videoContainer.requestFullscreen();
  }
  else if (videoContainer.msRequestFullscreen) {
      document.msFullscreenElement ? document.msExitFullscreen() : videoContainer.msRequestFullscreen();
  }
  else if (videoContainer.mozRequestFullScreen) {
      document.mozFullScreenElement ? document.mozCancelFullScreen() : videoContainer.mozRequestFullScreen();
  }
  else if (videoContainer.webkitRequestFullscreen) {
      document.webkitFullscreenElement ? document.webkitExitFullscreen() : videoContainer.webkitRequestFullscreen();
  }
  else {
      alert("全屏模式不受支持");
  }
}

4. 优化全屏状态下的控件样式

添加全屏状态的CSS规则,确保控件在全屏时清晰可见:

.video-container:fullscreen {
  display: flex;
  justify-content: center;
  align-items: center;
  background: #000;
}

.video-container:fullscreen video {
  max-width: 100vw;
  max-height: 100vh;
}

.video-container:-webkit-full-screen {
  display: flex;
  justify-content: center;
  align-items: center;
  background: #000;
}

.video-container:-webkit-full-screen video {
  max-width: 100vw;
  max-height: 100vh;
}

5. 修复交互细节

  • 合并重复的DOM元素获取变量
  • 将音量条事件改为input,实现实时调节
  • 优化进度条点击逻辑,使用getBoundingClientRect()提升全屏时的准确性

完整修改后的代码

HTML

<html data-bs-theme="dark" >
<head>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
  <style>
    .video-container {
      position: relative;
      width: 100%;
      max-width: 100%;
    }

    .controls {
      background: rgba(0,0,0,0.7);
      backdrop-filter: blur(5px);
    }

    .progress {
      height: 8px;
      background: rgba(255,255,255,0.3);
      border-radius: 4px;
      cursor: pointer;
      overflow: hidden;
    }

    .progress__filled {
      height: 100%;
      width: 0;
      transition: width 0.1s ease;
    }

    /* 隐藏默认视频控件 */
    video::-webkit-media-controls {
      display: none !important;
    }
    video::-moz-media-controls {
      display: none !important;
    }
    video::-ms-media-controls {
      display: none !important;
    }
    video {
      controls: none !important;
    }

    /* 全屏样式 */
    .video-container:fullscreen {
      display: flex;
      justify-content: center;
      align-items: center;
      background: #000;
    }

    .video-container:fullscreen video {
      max-width: 100vw;
      max-height: 100vh;
    }

    .video-container:-webkit-full-screen {
      display: flex;
      justify-content: center;
      align-items: center;
      background: #000;
    }

    .video-container:-webkit-full-screen video {
      max-width: 100vw;
      max-height: 100vh;
    }
  </style>
</head>
<body>
  <div class="container my-4">
    <div class="video-container position-relative">
      <video id="video" class="video w-100" playsinline>
        <source src="https://test.loro.ec/www/t/test.loro.ec/images/articles_blocks/608.mp4" type="video/mp4">
      </video>
      <div class="controls position-absolute bottom-0 w-100 p-3">
        <div class="row align-items-center">
          <div class="col-1">
            <button class="controls__button toggleButton btn" id="toggleButton" title="Toggle Play" onclick="togglePlay()">►</button>
          </div>
          <div class="progress col-5 p-0" id="progressId">
            <div class="progress__filled" style="background: rebeccapurple;"></div>
          </div>
          <div class="col-4 d-flex align-items-center gap-2">
            <i class="bi bi-volume-up"></i> 
            <input class="form-range flex-grow-1" type="range" id="volume-bar" title="volume" min="0" max="1" step="0.1" value="1">
          </div>
          <div class="col-1">
            <button class="btn" onclick="toggleFullScreen()"><i class="bi bi-arrows-fullscreen"></i></button>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script>
    const video = document.querySelector(".video");
    const toggleButton = document.getElementById("toggleButton");
    const progressBar = document.querySelector(".progress__filled");
    const progressContainer = document.getElementById('progressId');
    const volumeBar = document.getElementById('volume-bar');
    const videoContainer = document.querySelector('.video-container');

    function togglePlay() {
      if (video.paused || video.ended) {
        video.play();
      } else {
        video.pause();
      }
    }

    function updateToggleButton() {
      toggleButton.innerHTML = video.paused ? "►" : "❚❚";
    }

    function handleProgress() {
      const progressPercentage = (video.currentTime / video.duration) * 100;
      progressBar.style.width = `${progressPercentage}%`;
    }

    function scrub(e) {
      const rect = progressContainer.getBoundingClientRect();
      const scrubX = e.clientX - rect.left;
      const scrubTime = (scrubX / rect.width) * video.duration;
      video.currentTime = scrubTime;
    }

    volumeBar.addEventListener("input", function(evt) {
      video.volume = evt.target.value;
    });

    video.addEventListener("click", togglePlay);
    video.addEventListener("play", updateToggleButton);
    video.addEventListener("pause", updateToggleButton);
    video.addEventListener("timeupdate", handleProgress);

    progressContainer.addEventListener("click", scrub);
    let mousedown = false;
    progressContainer.addEventListener("mousedown", () => (mousedown = true));
    progressContainer.addEventListener("mousemove", (e) => mousedown && scrub(e));
    progressContainer.addEventListener("mouseup", () => (mousedown = false));
    progressContainer.addEventListener("mouseleave", () => (mousedown = false));

    function toggleFullScreen() {
      if (videoContainer.requestFullscreen) {
          document.fullscreenElement ? document.exitFullscreen() : videoContainer.requestFullscreen();
      } else if (videoContainer.msRequestFullscreen) {
          document.msFullscreenElement ? document.msExitFullscreen() : videoContainer.msRequestFullscreen();
      } else if (videoContainer.mozRequestFullScreen) {
          document.mozFullScreenElement ? document.mozCancelFullScreen() : videoContainer.mozRequestFullScreen();
      } else if (videoContainer.webkitRequestFullscreen) {
          document.webkitFullscreenElement ? document.webkitExitFullscreen() : videoContainer.webkitRequestFullscreen();
      } else {
          alert("全屏模式不受支持");
      }
    }
  </script>
</body>
</html>

关键改进点

  • 将视频与控件放入同一容器,确保全屏时两者同时被渲染
  • 使用绝对定位固定控件在视频底部,适配窗口和全屏状态
  • 隐藏浏览器默认控件,避免交互冲突
  • 优化进度条和音量条的交互体验
  • 适配多浏览器的全屏API和样式规则

内容的提问来源于stack exchange,提问作者Eliseo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 00:18:12