最稳方案是用 <input type="range"> 控制 clip-path: inset(),因其不依赖图片尺寸、无缩放/DPR适配问题、不卡顿;移动端需加 touch-action: none。

直接用 <input type="range"> 控制 clip-path: inset() 是最稳、最轻量的方案,不需要框架、不依赖图片原始尺寸、不卡顿,移动端加 touch-action: none 就能跑通。
为什么别用 left / transform 移动图层
靠移动上层图来“露出”下层图,本质是把两张图错开。一旦页面缩放、DPR 变化(比如 iPhone 的 2x 屏)、图片还没加载完,立刻出现白边、跳动或滑块失准。
-
transform在 Safari 和旧 Edge 渲染管线不一致,容易丢帧 -
left值依赖父容器宽度,但img.naturalWidth在load前是0,此时读尺寸全是错的 - 移动端 touch 拖拽时,
transform可能触发合成层切换,反而更卡
clip-path: inset() 怎么写才真正可控
推荐用 CSS 自定义属性 + inset() 组合,JS 只需更新变量,避免硬编码,也规避了 Safari 15.4+ 以下对百分比 inset() 支持不全的问题。
- 给上层图加:
.img-after { clip-path: inset(0 calc(100% - var(--p, 50)%) 0 0); } - JS 中只执行:
container.style.setProperty('--p', slider.valueAsNumber); - 不要用已废弃的
clip: rect(),它不支持百分比,现代浏览器基本不认 - 若需兼容 Safari 14 或更早,降级用
mask-image: linear-gradient(to right, black 0%, black var(--p, 50)%, transparent 0%),但维护成本高
移动端拖不动?先关掉 touch-action
iOS Safari 默认对 <input type="range"> 启用了触摸优化,和 clip-path 动画叠加时,常导致响应延迟或丢帧。
立即学习“前端免费学习笔记(深入)”;
- 给
<input>元素加 CSS:touch-action: none; - 确保父容器没设
will-change: transform或overflow: scroll,这些会干扰合成层调度 - 不用节流(throttle)——
input事件本身频率足够低,节流反而造成滞后感 - 监听
touchstart→ 记录初始位置;touchmove→ 用event.touches[0].clientX算位移;touchend→ 清理状态
图片没加载完就初始化?滑块一定错位
img.width 和 img.naturalWidth 在 load 事件前都是 0,此时计算裁剪值全是错的。拖动后图片加载完成,突然跳变是典型症状。
- 必须等两张图都触发
onload后再绑定input事件 - 给
<img>加loading="eager",防止懒加载打乱时序 - 用
img.naturalWidth && img.naturalHeight判断是否真正就绪,比仅靠onload更可靠(尤其缓存命中时) - 任一图加载失败,应设
slider.disabled = true并显示 fallback 提示
最容易被忽略的是:两张图必须严格等宽等高、完全重叠,且父容器设 position: relative 和 overflow: hidden —— 这不是样式问题,是布局逻辑前提。任何想绕过这个前提的“取巧”,最终都会在真机 Safari 或高 DPR 屏上翻车。



















