必须用 transform: translateX() 而非 left 或 margin-left,因其不触发重排、走 GPU 合成器,动画顺滑;需设固定宽度、避免 vw 混用、配合 overscroll-behavior 和 aria-hidden 保障体验与可访问性。

直接用 transform: translateX() 控制显隐,不改 display、不调 left,动画才顺滑不卡顿。
为什么必须用 transform: translateX() 而不是 left 或 margin-left
改 left 或 margin-left 会触发浏览器重排(layout),尤其在低端安卓 WebView 或 iOS Safari 下,滑动时明显掉帧、抖动。而 transform 只走合成器,GPU 加速,滚动/动画几乎无负担。
- 错误写法:
.sidebar { left: -280px; transition: left 0.3s; }—— 触发 layout,iOS 上易卡死 - 正确写法:
.sidebar { transform: translateX(-100%); transition: transform 0.3s ease; } - 务必给抽屉容器设明确宽度(如
width: 280px),否则-100%会按视口宽计算,小屏下位移过大 - 加
will-change: transform(可选)对长列表或旧设备有实际提升
translateX(-100%) 的位移值怎么写才可靠
写死 translateX(-300px) 看似简单,但缩放页面、横竖屏切换、不同 DPR 屏幕下容易错位。要用相对单位绑定自身尺寸。
- 首选:
transform: translateX(-100%),前提是父容器有固定宽度(width: 280px)且未被 flex 压缩 - 更灵活:
--drawer-width: 280px+transform: translateX(calc(-1 * var(--drawer-width))),方便媒体查询覆盖 - 别在
translateX()里混用vw或百分比又没约束父宽——iOS Safari 对vw渲染有延迟,滑动瞬间抽搐
抽屉展开后主内容区还在滚动?这是布局没锁死
不是 bug,是文档流没隔离。用户手指一划,背后内容照常滚动,体验断裂。关键不在禁 body 滚动,而在切断滚动上下文。
立即学习“前端免费学习笔记(深入)”;
- 抽屉父容器(即
display: flex的 wrapper)必须加overscroll-behavior: contain - 主内容区若本身可滚动,额外加
touch-action: pan-y,防 iOS 上滑抽屉误触主内容 - 别用
body { overflow: hidden }—— Safari 下会跳回顶部,且关闭后scrollX残留导致页面错位 - 真正要锁的是视口横向溢出:
document.body.style.overflowX = 'hidden'(打开时),关闭后设为空字符串,不是'auto'
移动端点击穿透和焦点管理最容易被忽略
抽屉弹出后,背后按钮仍能点中、键盘 Tab 还能跳进隐藏菜单——这既是交互漏洞,也是可访问性(a11y)硬伤。
- 抽屉激活时,主内容区加
aria-hidden="true",抽屉本身设aria-hidden="false" - 关闭按钮必须放在抽屉内部,且用
position: absolute; top: 1rem; right: 1rem,不然小屏可能被截 - 监听
Escape键关闭;Tab 键需手动限制在抽屉内循环(document.querySelectorAll("a, button, [tabindex]")) - 加
@media (prefers-reduced-motion: reduce) { .sidebar { transition: none; transform: none; } },不是锦上添花,是合规底线
最麻烦的从来不是滑动动画本身,而是抽屉收起后焦点没回到触发按钮、屏幕阅读器读不出状态变化、或者 iOS 上 touchmove 穿透到背后滚动——这些全靠 HTML 语义 + CSS 状态 + 少量 JS 协同,缺一不可。


















