用纯HTML+CSS实现可拖拽Before/After对比需解耦裁剪与拖动:容器relative,两张图absolute覆盖;After图用动态width遮罩或clip-path inset裁剪;PC端监听input事件,移动端绑定touchstart/move/end并preventDefault、用touches[0].clientX;图片加载完成后再初始化,兼容性差时选遮罩层+GPU加速。

如何用纯 HTML + CSS 实现可拖拽的 Before/After 图片对比
不能只靠 <input type="range"> 模拟滑块位置,必须让滑块拖动时实时裁剪右侧图片——核心是用 clip-path 或绝对定位遮罩层控制可见区域,且要兼容移动端 touch 事件。
常见错误是直接用两个 <img> 并排 + overflow: hidden 容器,结果滑块拖动时边缘撕裂、缩放失真、iOS 上卡顿。根本原因是没把“裁剪边界”和“拖动反馈”解耦。
- 容器设为
position: relative,内部两张图都设position: absolute; top: 0; left: 0; width: 100%; height: 100% - 左侧图(Before)保持完整显示;右侧图(After)外层套一个
<div class="after-mask">,宽度由 JS 动态设置为滑块位置百分比 - 滑块元素本身用
<input type="range">,但隐藏原生样式,用伪元素或额外<div>做视觉手柄 - 监听
input事件(不是change),保证拖动过程实时响应
移动端 touch 拖动不生效或跳变的修复方法
PC 端用 mousedown/mousemove 能跑通,但 iOS Safari 和安卓 Chrome 默认会触发页面滚动或缩放,导致滑块“抓不住”。必须显式阻止默认行为并手动绑定 touch 事件链。
- 给滑块容器加
touch-action: none,禁用浏览器默认 touch 行为 - 监听
touchstart→ 记录初始位置和当前value;touchmove→ 计算横向位移,换算成 0–100 范围更新value和遮罩宽度;touchend→ 清理状态 - 不要依赖
event.clientX,改用event.touches[0].clientX,避免多点触控干扰 - 在
touchmove中调用event.preventDefault(),否则部分安卓机型仍会触发 scroll
clip-path vs 绝对定位遮罩:性能与兼容性怎么选
clip-path: inset(0 0 0 X%) 写法简洁,但 Safari 旧版本(≤15.4)对动态 clip-path 动画支持差,拖动时掉帧明显;而用遮罩层 width 控制更稳定,但需确保父容器 overflow: hidden 且子图无 margin/padding。
立即学习“前端免费学习笔记(深入)”;
- 如果只需支持 Safari 16+ / Chrome 100+,优先用
clip-path:减少 DOM 节点,CSS 动画更顺滑 - 若需兼容 iOS 15 或 Android 11 以下,用遮罩层方案,配合
transform: translateZ(0)强制 GPU 加速 - 避免在遮罩层上设
border或box-shadow,会导致裁剪边缘模糊 - 图片务必设
display: block,防止底部留白影响高度计算
图片加载完成前滑块错位或空白的预防措施
组件初始化时若图片未加载完,offsetWidth 可能为 0,导致滑块初始位置计算错误,或遮罩层宽度异常。不能等 window.onload,太晚;也不能只监听单张图的 load,两张图加载时机不同。
- 用
Promise.all([img1.decode(), img2.decode()])等待两张图解码完成(注意 IE 不支持decode(),需 fallback 到img.onload) - 初始化时先设滑块
value="50",但遮罩层宽度暂不更新,等图片就绪后再同步一次 - 给图片加
loading="eager",避免懒加载干扰交互初始化 - 容器加
min-height: 200px占位,防止内容闪动
clientX 与元素 getBoundingClientRect() 返回值的像素比不一致问题,尤其在 pinch-zoom 后。这时候得用 window.devicePixelRatio 校正,但多数项目直接限制用户禁止缩放更省事。


















