如何使自定义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
相关产品推荐
相关产品推荐

