你以为播放器样式只能靠 JS 监听?今天 CSS 七族伪类把这件事彻底原生化了

配了五年播放器,每次想让视频播的时候样式跟着变,都要在 JS 里写 play/pause 事件监听,classList.add/remove 跑一套——现在 CSS 七族媒体状态伪类把这件事彻底原生化了。

播放器状态一变,样式就得跟着变,这件事前端工程师没少做。传统做法是 JS 监听事件,手动 toggle class,再在 CSS 里写 .is-playing/.is-paused 样式。

一个播放心视频要写多少?先绑 play 事件,再绑 pause 事件,可能还要管 ended、waiting、volumechange 事件。每加一个播放器就得复制粘贴一套。这还是只有「播放/暂停」两个状态的简单场景。

Chrome 152 稳定版带来了完整的媒体元素状态伪类家族:

/* 正在播放(未暂停、未结束) */
video:playing { }

/* 暂停中(含初始未播放状态) */
video:paused { }

/* 跳转/拖动进度条时 */
video:seeking { }

/* 正在缓冲(网络卡住) */
video:buffering { }

/* 缓冲失败/卡死 */
video:stalled { }

/* 静音状态 */
video:muted { }

/* 音量被系统锁定(如 iOS) */
video:volume-locked { }

浏览器自动维护这些状态,CSS 负责样式,不用写一行 JS。

实战一:播放按钮浮层

想让视频暂停时显示一个大大的播放图标:

.player video:paused::after {
  content: "▶";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 3rem;
  background: rgb(0 0 0 / 0.4);
  color: white;
  pointer-events: none;
}
video:playing::after { display: none; }

三行 CSS,浏览器自动判断显示/隐藏,播放器切到播片状态图标就消失。

实战二:静音图标联动

用相邻兄弟选择器,纯 CSS 让音量图标跟着视频静音状态走:

.volume-icon::before { content: "🔊"; }
video:muted ~ .volume-icon::before { content: "🔇"; }

不用 JS 写事件监听,CSS 直接读媒体元素状态。

实战三:缓冲指示器动画

卡顿时自动出现脉冲动画:

@keyframes pulse {
  50% { opacity: 0.5; }
}
.record-indicator { display: none; }
video:buffering ~ .record-indicator {
  display: block;
  animation: pulse 1s ease-in-out infinite;
}

用户网络一卡,CSS 自动切到缓冲状态,动画就起来了。

浏览器支持现状

伪类 Chrome Firefox Safari
:playing 152 150 15.4
:paused 152 150 15.4
:muted 152 150 15.4
:buffering 152 150 ×
:seeking 152 150 ×
:stalled 152 150 ×
:volume-locked 152 150 ×

Safari 支持 :playing:paused:muted 三个,:buffering 等较新的还在跟进。

渐进增强写法

@supports (selector(video:playing)) {
  video:paused::after { display: grid; }
} @supports not (selector(video:playing)) {
  .is-paused::after { display: grid; }
}

先写伪类方案,Safari 用 @supports not 兜底 JS class 方案。

三个坑

一、:paused 会匹配初始未播放状态。海报图 + 播放按钮的场景里,页面刚加载时视频就是 :paused,所以播放按钮浮层的设计要区分「未播放」和「用户主动暂停」两种场景——前者通常不需要显示暂停图标。

二、Safari 暂不支持 :buffering / :seeking。这两个较新,全量支持要等到 Safari 跟进。用 @supports (selector(video:buffering)) 做条件加载,别一股脑全上。

三、:volume-locked 只在移动端有意义。桌面浏览器音量不会被系统锁定,这个伪类始终不匹配,给桌面写样式时别指望它。

三个下一步

一、找一个现有播放器的 HTML,把 JS 里的 play/pause 事件监听换成 CSS 伪类,验证行为是否一致。

二、给缓冲指示器写一个 CSS 动画,搭配 :buffering 伪类,做一个网络卡顿时自动出现的「加载中」效果。

三、用 @supports selector(video:playing) 做渐进增强,让 Safari 用户走 JS class 兜底方案,其他用户走纯 CSS。

评论区

0 条评论

登录后可评论。