← 📜 Scroll · 滚动怎么动、长什么样、装得下多少 / scroll-axis-lock:轴锁与对角滚动 待审核 2 / 9
scroll-axis-lock · 轴锁

scroll-axis-lock:轴锁与对角滚动

在一张放大后的地图上斜着拖动,期望的结果是视口沿手指方向斜着走。实际结果常是先横一段、再竖一段的楼梯路径:浏览器把手势投影到了位移较大的那条轴上,另一维分量被整段手势丢弃。这条启发式叫 scroll railing(轴锁),过去无法关闭,CSS Overflow Level 5 给了它一个开关。

1 · 轴锁的判定与代价

判定发生在手势的开头。规范给的例子是一次产生 500px 垂直位移、3px 水平位移的手势:两轴量级相差两个数量级,用户的意图显然是纯垂直滚动,那 3px 是握持手指的抖动。把水平分量丢掉,阅读流里的长列表就不会一边往下滚一边横向漂移。判定一旦作出,就对整段手势有效,中途改变拖动方向也不会解锁。

代价出现在内容本身是二维的场合。地图、放大后的图片、大幅工程图与原理图、无限画布类设计工具、宽表格与数据网格、360 全景与 3D 模型的平移,这些界面里对角移动是常规操作而非误触。轴锁要求用户先给出一个角度足够端正的起手势才能拿到斜向滚动,否则就得横竖交替地分两次拖。

判定用的具体比例并不在规范里,规范只说「显著更多」(significantly more),把阈值留给 UA。§5 记下了这一点带来的后果。

图 1-1 · 输入向量与轴锁后的实际滚动轨迹。虚线为指针原始路径,实线为投影后的轨迹。可切换锁定策略、调判定比例,观察被丢弃的分量像素数。

2 · 属性与取值

名称 scroll-axis-lock
取值 auto | none
初始值 auto
适用于 滚动容器元素
是否继承
计算值 指定值
动画类型 离散

auto 允许 UA 锁轴,也就是现有行为;none 要求 UA 不得锁轴,滚动严格跟随输入向量。

.canvas {
  overflow: auto;
  overscroll-behavior: contain;
  scroll-axis-lock: none;
}

overscroll-behavior: contain 常与它同时出现,但两者管的是不同的事:前者管滚到尽头后多余的滚动量向不向祖先传播(见 overscroll-behavior),后者管手势的方向分量在进入滚动引擎时保不保住。二维画布通常两条都要。

不继承是刻意的。同一个页面里,地图组件需要 none,而它下方的说明文字仍应享有轴锁;若属性继承,每个内层滚动容器都得写一次 auto 复位。

注 · 这件事无法用 touch-action 做到。touch-action 工作在输入拦截阶段,决定某个手势是否交给滚动;轴锁工作在滚动引擎内部,决定已经交进来的 delta 怎么用。而且 touch-action 只管触屏直接交互,触控板与滚轮不在它的管辖范围,而轴锁对这三者都生效。属性名最初提议为 overflow-axis-lock,为与 scroll-snap-type 等滚动相关属性对齐而改为现名。

3 · 真实容器上的行为

轴锁的可观测特征是量化的:滚动轨迹的采样点里,两轴同时变化的比例。轴锁生效时这个比例接近零(每次滚动事件只有一条轴在动),关掉后对角手势会让绝大多数采样点两轴同时变化。

图 3-1 · 一个二维滚动容器与它的 scrollLeft / scrollTop 轨迹缩略图。属性探针给出当前浏览器是否识别 scroll-axis-lock;双轴采样占比是轴锁是否生效的量化读数。

这个 lab 画的是浏览器的真实行为而非模拟:不支持该属性的浏览器上切换下拉框不会有任何变化,轨迹图仍如实记录轴锁的楼梯形状。鼠标滚轮几乎无法产生对角 delta,要看出差别需要触控板或触屏。

4 · 支持度与降级

Chromium 153 是首个实现,六个 Blink 平台(Windows / Mac / Linux / ChromeOS / Android / WebView)默认开启,Web Platform Tests 已覆盖(css/css-overflow/scroll-axis-lock.html)。Gecko 侧的 standards-position 处于 unscreened、尚无结论,WebKit 未表态(兼容性据规范、chromestatus 与 blink-dev intent,核对于 2026-08)。

降级不需要 @supports。初始值是 auto,而 auto 就是所有现存浏览器的既有行为,因此不识别这条声明的浏览器把它整条丢弃后,得到的正是原本的表现。写 scroll-axis-lock: none 是纯粹的渐进增强,无需特性检测分支。

5 · 存疑:阈值不在规范里

规范与 explainer 都没有给出判定阈值,只给了 500px 比 3px 这个示意性的例子和「显著更多」这句措辞。这意味着两件事:图 1-1 的判定比例是一个占位参数而非 Chromium 的真实数字,读者不应把它当实现细节;跨引擎的 auto 行为也不保证一致,将来若 Gecko 与 WebKit 跟进,能对齐的只有 none

本页写作时手边的 Chrome 是 151,CSS.supports('scroll-axis-lock', 'none') 返回 false,属性尚未落地,图 3-1 的两个取值在该版本上无从区分(核对于 2026-08)。同一次探测里 CSS.supports('overscroll-behavior', 'chain') 返回 true,与 overscroll-behavior 一页记的 Chrome 150 新增值吻合,可作为探测本身有效的旁证。

探测顺手撞到一个坑:CSS.supports('scroll-axis-lock', 'auto') 在未实现的浏览器上同样返回 false。检测不能借 auto 这个默认取值来做,两个取值都要等属性本身落地才为真。

属性与 scroll-snap 同时使用时的行为,规范与 explainer 均未说明。首个实现的手感也还不算稳:Adam Argyle 在两个 demo 上试过后认为对角滚动仍显笨拙,并为此提了一份性能回归报告。

6 · 参考文献

  1. CSS Working Group. CSS Overflow Module Level 5, §Scroll axis locking. W3C Editor's Draft. drafts.csswg.org
  2. Argyle, A. (2026-08-10). Scroll Axis Lock. nerdy.dev. nerdy.dev
  3. explainers-by-googlers. scroll-axis-lock explainer. github.com