最轻量无JS的Before/After滑块方案是用clip-path+input[type="range"]纯CSS实现,核心为clip-path: inset(0 X% 0 0)动态裁剪右图,兼容Chrome57+/Firefox54+/Safari15.4+。

用 clip-path 实现原生、无 JS 的 Before/After 滑块
纯 CSS 方案最轻量,兼容性足够(Chrome 57+、Firefox 54+、Safari 15.4+),不需要监听鼠标或触摸事件,也不依赖第三方库。核心是把两张图叠在一起,用 clip-path 动态裁剪右侧图的显示区域,再通过 input[type="range"] 控制裁剪比例。
常见错误是直接用 overflow: hidden + 绝对定位移动图层——这在高 DPI 屏或缩放页面时容易出现 1px 错位;而 clip-path: inset(0 X% 0 0) 是基于容器尺寸计算的,更稳定。
- 两张图必须尺寸完全一致(推荐用同一张图的两个版本,或用
object-fit: cover统一约束) -
input的min/max设为 0 和 100,value初始设为 50(即中位) - 滑块 thumb 样式需重置:Chrome 用
::-webkit-slider-thumb,Firefox 用::thumb,Safari 用::-webkit-slider-thumb - 为避免拖动时页面滚动,给
input加touch-action: none
处理 Safari 15.3 及更早版本的 clip-path 兼容问题
Safari 15.3 对 inset() 的百分比支持不完整,会出现裁剪失效或跳变。此时应降级为 clip-path: polygon(),用 JavaScript 动态更新点坐标——但只在检测到旧 Safari 时才启用,其他浏览器仍走纯 CSS 路线。
判断方式不是 UA 字符串,而是用 CSS.supports('clip-path', 'inset(0)')。如果返回 false,就改用 polygon(0 0, calc(X%) 0, calc(X%) 100%, 0 100%) 并绑定 input 的 input 事件。
立即学习“前端免费学习笔记(深入)”;
- 不要用
change事件——拖动时不会实时触发,用户会感觉卡顿 -
polygon()中的calc()必须带单位(如calc(50% - 1px)合法,calc(50% - 1)非法) - 移动端需额外监听
touchmove并preventDefault(),否则 iOS Safari 会触发弹性滚动
图片加载失败时的降级策略
对比组件最怕一张图加载失败,导致空白或错位。不能只靠 onerror 替换 src,因为两张图加载时机不同,容易出现闪动或状态不一致。
- 给两图都加
loading="lazy",但用IntersectionObserver在进入视口前预加载,确保同时就绪 - 用
img的naturalWidth检查是否加载成功,任一为 0 就隐藏整个容器,显示提示文案<p class="ba-error">图片加载失败</p> - 避免用背景图实现——无法获取自然尺寸,也无法可靠监听加载状态
- 如果服务端支持,可提前生成带 hash 的响应头
Cache-Control: immutable,减少重复加载失败概率
响应式与高 DPI 下的像素对齐细节
在 Retina 屏上,clip-path 的裁剪边缘容易发虚,尤其当容器宽高不是整数物理像素时。这不是 bug,而是亚像素渲染的必然结果。
解决方法不是强行取整,而是让容器本身占据整数设备像素:给包裹容器加 transform: translateZ(0) 触发硬件加速,并配合 image-rendering: -webkit-optimize-contrast(仅 Safari)和 image-rendering: crisp-edges(Chrome/Firefox)来抑制插值模糊。
- 不要用
width: 100vw作容器宽度——滚动条存在时会导致横向溢出,改用width: 100%或max-width: 100% - 移动端竖屏转横屏时,
input[type="range"]容易被系统键盘遮挡,建议监听resize事件,当window.innerHeight突然变小时,临时将滑块 position 固定在顶部 - 如果图片含重要文字或线条,务必检查对比度是否满足 WCAG 2.1 AA 标准(文本至少 4.5:1),否则滑块位置变化时可读性会断崖下降
实际项目里最容易被忽略的是加载状态同步和 Safari 旧版降级的边界条件——比如用户快速拖动滑块时图片还没加载完,或者在 iOS 15.1 上 clip-path 支持检测返回 true 但实际渲染异常。这些得靠真实设备真机调试,模拟器跑不出来。



















