ul必须absolute定位且父容器relative+overflow:hidden,li宽度需设为可视区固定像素值,避免inline-block间隙和vw陷阱,移动端推荐transform替代left实现精准滚动。

ul 和 li 必须用 absolute + relative 配合才能横向滚动
轮播图里 ul 不能靠默认文档流排列,否则 li 会竖着堆叠或换行——根本没法“一排铺开”。核心是让 ul 脱离文档流并允许左右位移,同时保证父容器能裁剪溢出部分。
常见错误是只给 ul 加 position: absolute,但忘了父容器(比如 .focus 或 .container)必须设 position: relative。否则 ul 的 left 值会相对于 body 计算,一动就飞出可视区。
-
ul:必须position: absolute,且top: 0、left: 0(起始对齐左上),宽度要撑满所有图片(如width: 600%或width: calc(100% * N)) -
li:不需要单独设position,但必须设固定宽度(通常等于父容器宽度),且用float: left或display: inline-block横向排列;若用flex,则ul要加display: flex,但此时left位移失效,得改用transform: translateX() - 父容器:必须
position: relative+overflow: hidden,否则滚动时内容会露出来,且ul的绝对定位基准丢失
li 宽度必须等于轮播容器宽度,不能靠百分比自动算
很多人写 li { width: 100% },结果发现图片错位、切换跳帧——因为 li 的 100% 是相对于 ul 的宽度,而 ul 又被设成了超宽值(比如 600%),导致每个 li 实际宽得离谱。
正确做法是把 li 宽度写死为轮播可视区宽度,也就是父容器的 width 值。例如容器宽 800px,那就写 li { width: 800px };如果容器响应式,就得用 JS 动态读取并赋值,或者改用 flex + translateX 配合 getBoundingClientRect().width 计算。
立即学习“前端免费学习笔记(深入)”;
- 静态布局:直接写像素值,最稳,比如
li { width: 800px; height: 450px; } - 响应式陷阱:用
li { width: 100vw }看似聪明,但vw是视口宽,不是容器宽,容易错位 - Flex 场景下:
ul设display: flex,li设flex: 0 0 100%,此时每个li占满容器宽度,但注意:不能再用left控制位移,得用transform: translateX(-800px)这类方式
ul 宽度计算必须包含所有 li,且不能依赖 display: inline-block 的间隙
当用 display: inline-block 排 li 时,HTML 换行或空格会产生约 4px 的额外间隙,导致 ul 实际宽度比预期多出若干像素,left 位移就会偏移——切到第二张图时可能只移了 796px 而不是 800px,画面卡半张。
解决方法不是调小 li 宽度去凑,而是从根上消灭间隙:
- 删 HTML 中
li标签间的换行和空格(不现实但最彻底) -
ul { font-size: 0 },再给li单独设font-size: 16px(推荐) - 改用
float: left,然后在ul上清浮动(overflow: hidden或伪元素) - 直接上
flex,天然无间隙
另外,ul 总宽 = li 个数 × 单个 li 宽度,别漏掉这个乘法。写成 width: calc(100% * 5) 比手算 500% 更安全,尤其图片数量动态变化时。
移动端 touch 位移时,ul 的 left 值容易因小数精度错位
用 touchstart/touchmove 实现拖拽轮播时,经常出现最后一张图切不全、回弹不到位——根源是 ul.style.left 赋值时用了浮点数(比如 -799.9999999999999px),浏览器渲染四舍五入后偏差累积。
关键处理是手动截断小数位,尤其在设置 left 前做一次 Math.round():
let offsetX = Math.round(startX - currentX);
ul.style.left = `-${offsetX}px`;更稳妥的做法是全程用整数坐标运算,或改用 transform: translateX() 替代 left,因为 transform 对 sub-pixel 渲染更友好,且开启 GPU 加速。
顺带一提:如果用 left 切换,记得每次更新前先 ul.style.transition = 'none'(防止拖拽中动画干扰),松手后再恢复过渡;而 transform 方案可以直接保留 transition,逻辑更干净。



















