直接对 video 内置控件加 transition 无效,因浏览器禁用对 ::-webkit-media-controls 等伪元素的 CSS 过渡;正确做法是用自定义遮罩层(.video-controls-overlay)配合 JS 控制显隐与事件同步。

直接给 video 元素的内置控件加 transition 无效——浏览器根本不允许对 ::-webkit-media-controls 这类伪元素做 CSS 过渡,强行写也不会生效。
为什么不能直接 transition video 控件
Chrome / Edge 的原生控制条是 Shadow DOM 内部渲染的独立 UI 组件,CSS 无法穿透并对其属性(如 opacity、visibility)做渐变动画;Safari 更彻底,连伪元素选择器都限制极严。你写的 video::-webkit-media-controls { opacity: 0; transition: opacity 0.3s; } 看似合理,实际被忽略。
- 浏览器不把控件当作普通 DOM 节点管理,过渡引擎压根不介入
- 即使 hack 出临时可见性(比如用
transform: scaleY(0)),动画过程会卡顿或闪烁 - 移动端 WebView(尤其旧版 Android)可能直接禁用所有伪元素样式
正确做法:用遮罩层 + JavaScript 控制显隐状态
本质是绕过原生控件,用一层自定义 DOM 遮罩覆盖在视频上,再控制这层遮罩的透明度。关键在于状态同步和事件隔离:
- 监听
video的play、pause、timeupdate事件,触发遮罩显示 - 用
setTimeout启动空闲计时器,超时后给遮罩加show-controls类 - 必须同时监听
mousemove和touchstart,且用'ontouchstart' in window判断环境,避免双事件冲突重置计时器 - 遮罩层需设
pointer-events: none(默认),仅在显示时切为auto,否则会拦截点击播放
示例结构:
立即学习“前端免费学习笔记(深入)”;
<div class="video-container"> <video src="a.mp4" muted autoplay></video> <div class="video-controls-overlay"></div> </div>
CSS 中只对 .video-controls-overlay 做 opacity 过渡,不碰 video 本身。
移动端 touch 事件的坑必须填平
iOS 和多数安卓浏览器没有 “鼠标离开” 概念,纯靠 mouseleave 隐藏控件会导致它永远挂着。真实场景中:
-
touchstart必须触发一次显控逻辑,并立即重置空闲倒计时 - 不能和
mousemove共享同一套计时器变量,否则手指刚点下就触发隐藏 - 部分安卓 WebView 渲染遮罩层有延迟,建议在移除
show-controls类后加setTimeout(() => {}, 100),留出样式重绘间隙 - 若使用
requestVideoFrameCallback做帧同步控制,注意它不适用于遮罩层动画,只用于视频内容切换
最易被忽略的一点:遮罩层的 z-index 必须高于原生控件(通常要 ≥ 10),否则在某些 Chrome 版本里会被盖住——这不是 bug,是浏览器故意让原生控件拥有更高合成层优先级。


















