最稳方案是用 控制 clip-path: inset(),因其轻量、兼容性好且不受dpr、加载延迟影响;其他方式如 left 或 transform 易导致错位、卡顿或白边。

直接用 <input type="range"> 控制 clip-path: inset() 是最稳、最轻量的方案,不需要任何框架或插件。其他方式要么错位、要么卡顿、要么兼容性差。
为什么不能用 left 或 transform 移动上层图片
靠移动图层位置来“露出”下层图,本质是把两张图强行错开——只要容器宽度变化、图片加载延迟、DPR 不同(比如 iPhone 的 2x/3x 屏),就会立刻出现白边、跳动或滑块失准。
- 浏览器对
transform的渲染管线不统一,Safari 和旧 Edge 容易丢帧 -
left值依赖父容器宽度,但img的自然宽高未加载完时读不到真实尺寸 - 移动端 touch 拖拽时,
transform可能触发合成层切换,反而更卡
clip-path: inset() 怎么写才真正可控
推荐用 CSS 自定义属性 + inset() 组合,避免硬编码、方便 JS 更新,且兼容现代浏览器(Chrome/Firefox/Edge ≥105,Safari ≥15.4)。
- 给上层图加样式:
.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%),但会增加维护成本
图片没加载完就初始化?滑块一定错位
img.width 和 img.naturalWidth 在 load 事件前都是 0,此时读取尺寸、计算裁剪值全是错的。拖动后图片加载完成,突然跳变是典型症状。
- 必须等两张图都触发
onload后再绑定input事件 - 给
<img>加loading="eager",防止懒加载打乱时序 - 监听
img.naturalWidth && img.naturalHeight,比仅靠onload更可靠(尤其当图片来自缓存时) - 任一图加载失败,应设
slider.disabled = true并显示 fallback 提示
移动端拖不动或卡顿?先关掉 touch-action
iOS Safari 默认对 input[type="range"] 启用了触摸优化,但和 clip-path 动画叠加时,常导致响应延迟或丢帧。
- 给 range 元素加 CSS:
touch-action: none; - 确保容器没设
will-change: transform或overflow: scroll,这些会干扰合成层调度 - 不用节流(throttle)——
input事件本身频率足够低,节流反而造成滞后感 - 如果仍卡,检查是否在
input回调里做了 DOM 查询(如反复querySelector),应提前缓存引用
真正难的不是写出来,而是让滑块在不同 DPR、不同加载状态、不同 Safari 版本下都保持像素级对齐——这要求所有尺寸逻辑都锚定在 naturalWidth/Height 和 CSS 自定义属性上,而不是任何运行时测量或 CSS 百分比推算。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











