用 transform: translatex() 而非 left 或 margin-left 是因后者触发重排,前者由 gpu 加速且不触发布局;需配合 will-change、固定容器宽度及正确定时器管理、无障碍语义化和防误触滑动逻辑。

为什么用 transform: translateX() 而不是 left 或 margin-left
直接改 left 或 margin-left 会触发浏览器重排(reflow),尤其在轮播图频繁切换时,容易卡顿、掉帧。而 transform: translateX() 属于合成层操作,由 GPU 加速,不触发布局计算,性能更稳。
常见翻车点:
– 把图片容器设为 position: relative 后用 left 动画,页面滚动或缩放时位移错乱
– 没加 will-change: transform 或 transform: translateZ(0),导致部分安卓 WebView 不启用硬件加速
– 容器宽度没设死(比如用 width: 100% 但父级不定宽),getBoundingClientRect().width 每次读出来不一样,位移距离算不准
setInterval 自动轮播的定时器管理陷阱
setInterval 看似简单,但漏清定时器是轮播图“突然跳回第一张”的最常见原因——比如用户手动切到第 3 张后,旧定时器仍在运行,3 秒后仍按原索引执行 currentIndex++,结果切到第 4 张,但实际只有 3 张,就溢出重置了。
正确做法:
– 每次手动切换(点按钮、点指示点、滑动结束)后,先 clearInterval(this.timer)
– 再用新 currentIndex 重新 setInterval
– 切换前加 isTransitioning 锁,避免动画未完成又触发下一轮位移
– transition 的时长必须和 setInterval 间隔对齐;比如设了 transition: transform 0.4s ease,自动轮播间隔至少得 ≥ 0.5s,否则视觉上会“抽搐”
无障碍与语义化不能靠 CSS 隐藏来凑
用 display: none 或 visibility: hidden 隐藏非当前项,会让屏幕阅读器完全感知不到这些图片,违反 WCAG 2.1。轮播图不是“只读当前一张”,而是“列表中的一张”,必须保留在可访问树里。
合规写法:
– 外层必须用 <ul class="carousel-list"></ul>,不能用 <div> 套一堆 <code><img>
– 每个轮播项用 <li class="carousel-item">,并同步控制 aria-hidden="false"(当前项)和 aria-hidden="true"(其余项)
– alt 文本不能是“图片1”,得写清动作和价值,例如 alt="iPhone 16 Pro 黑色款现货发售,下单立减800元"
– 导航按钮必须是 <button></button>,不是 <div onclick>,否则键盘用户无法聚焦或回车触发<h3>移动端 touch 滑动的边界判断必须防误触</h3>
<p>只比对 <code>touchstart.clientX 和 touchend.clientX 差值,手指斜着划、Y 方向偏移大时,极易误判为左右滑动,导致页面意外横向滚动或轮播误触发。
安全做法:
– 在 touchstart 记录 startX 和 startY
– touchend 时同时算 Math.abs(dx) 和 Math.abs(dy),仅当 Math.abs(dx) > Math.abs(dy) 且 Math.abs(dx) > 30 才视为有效横向滑动
– 滑动结束时,不直接切图,而是根据剩余偏移量做吸附:若 Math.abs(offset) > width * 0.3 才切换,否则回弹到当前图位置
– 必须在 touchmove 中调用 e.preventDefault(),否则 iOS Safari 会默认触发页面滚动
最易被忽略的是:所有状态变更——currentIndex、translateX、aria-hidden、指示点高亮、按钮禁用态——必须在同一个同步函数里完成。拆成多个 setTimeout 或事件回调,哪怕只差几毫秒,屏幕阅读器或动画引擎就可能读到中间态。











