📜 Scroll · 滚动怎么动、长什么样、装得下多少
滚动这件事有三个面。第一面是滚动怎么动:滚到尽头多余的滚动量去哪、跳转是瞬移还是平滑、斜向手势的另一维分量保不保得住、上方内容变高时画面跳不跳、松手后停不停得整齐——这些由几个常被忽视的 CSS 属性声明式地决定,各自只有两三个取值,用对了能消除一整类恼人的体验问题。
第二面是滚动条本身长什么样:从 CSS 换肤,到隐藏原生用 DOM 自绘,再到 Canvas 整条画——代价与能力递增。
第三面是滚动装得下多少:当数据大到十亿、万亿行,一次性渲染与加载都不可行时,虚拟滚动如何只渲染可见的几十行、只取看过的数据,还能绕过元素高度上限滚得动。
本系列每页讲一个属性 / 一族属性 / 一种做法,配一个可以直接拖动、点击的 demo,在你浏览器里跑真实行为。
overscroll-behavior;点目录 / 回到顶部要平滑过渡 → scroll-behavior;地图 / 无限画布 / 宽表要斜着拖而不走楼梯 → scroll-axis-lock: none;要在程序滚动停下后再接动画 / 判断有没有被打断 → await scrollTo();图片加载导致正在看的内容乱跳 → overflow-anchor;carousel / 整屏分页要停得整齐 → scroll snap;sticky 贴顶变身 / 吸附点亮 / 边缘渐隐这类「看滚动态换样式」→ scroll-state 容器查询;想换滚动条外观、做进度条 / 章节刻度式滚动条 → 自定义滚动条;十万行以上的长列表 / 大表卡顿 → 虚拟滚动。滚动怎么动 · 行为
这一类属性不改滚动条长相,只管滚动的动作本身:溢出的滚动量去哪、跳转瞬移还是平滑、上方变高时画面跳不跳、松手后停不停得整齐,以及让样式直接读出容器此刻的滚动态。
overscroll-behavior:滚到底之后,溢出的滚动去哪
一个属性同时管两件独立的事:滚动传播(嵌套容器滚到头,多余滚动量是否「漏」给祖先、带动整页)与边界默认效果(下拉刷新 / 橡皮筋回弹)。auto / contain / none 加上 Chrome 150 新增的 chain,四个关键字正好填满一张 2×2 表——动手玩嵌套滚动,看 contain 如何把滚动关在弹层里。
scroll-axis-lock:轴锁与对角滚动
触屏与触控板上斜向拖动,浏览器会把手势投影到位移较大的那条轴,另一维分量整段丢弃,路径走成楼梯。这条启发式挡的是阅读流里的误漂,代价落在地图与无限画布这类二维界面上。scroll-axis-lock: none 关掉它。
scroll-behavior:让跳转滚动从瞬移变平滑
点目录跳转、scrollIntoView()、改 #hash 这类程序触发的滚动默认瞬移,scroll-behavior: smooth 给它加平滑补间。动手体验,看 CSS 默认值与 JS 逐次 behavior 选项谁覆盖谁,以及为何要为 prefers-reduced-motion 退回瞬时。
scrollTo() 返回 Promise:等程序滚动停下再动手
scroll-behavior 让程序滚动变平滑,却没告诉你它何时到位。Chrome / Edge 150 起 scrollTo() / scrollBy() / scroll() 返回一个 Promise,滚动停下时兑现 { interrupted }——「滚到位再淡入某块内容」这类衔接动画可直接 await 出来。动手看平滑滚动结束后工具条淡入,以及快速二次点击时 interrupted: true 的打断,附特性检测与 scrollend 降级。
overflow-anchor:上方内容变高时别让画面乱跳
图片加载 / 内容注入让上方变高,你正看的行突然下跳——scroll anchoring 是浏览器的默认补偿,悄悄调 scrollTop 把你看的内容留在原地。动手在顶部插内容,对比 auto(补偿)与 none(退出,把控制权交给无限滚动 / 聊天那种自管滚动的逻辑)。
CSS Scroll Snap:让滚动整齐地停靠
carousel / 整屏分页希望松手后停在整齐位置。围绕三个角色(snap-container / snapport / snap-area)的五个属性——scroll-snap-type / scroll-padding / scroll-snap-align / scroll-margin / scroll-snap-stop——拖一个横向 carousel,看 mandatory 与 proximity 的手感差别。
scroll-state:让样式读出容器此刻的滚动态
container-type: scroll-state 把容器的滚动态暴露给样式层,后代用 @container scroll-state(...) 按态换样式,零脚本。三种查询各对应一个常见需求:stuck(sticky 贴住边没有)做贴顶变身、snapped(是不是当前吸附的那个)给 carousel 点亮、scrollable(某方向还有没有可滚内容)做边缘渐隐提示——三个可交互 demo + 渐进增强降级。
滚动条长什么样 · 外观
另一面是滚动条本身的样子。从 CSS 换肤到 DOM、Canvas 自绘,三档递进地把滚动条的外观与交互逐步接管。
三种自定义滚动条的方式
换皮肤 / 接管交互 / 整条自绘三档递进:::-webkit-scrollbar 与标准 scrollbar-color 只改外观;DOM 隐藏原生用 div 自绘 track / thumb;Canvas 整条画在画布上配 LERP 阻尼。三个 demo 滚同一段文本对比手感。
滚动装得下多少 · 海量数据
前两面都还在样式与交互行为的范畴。第三面是工程问题:当数据本身大到十亿、万亿行,一次性渲染与加载都会崩,虚拟滚动如何用切片、懒加载与滚动条缩放,只靠原生能力把它滚动起来。
虚拟滚动:在浏览器里滚动十亿行
数据大到十亿、万亿行时,全量渲染撑爆 DOM、全量加载撑爆内存、连一根够高的滚动条都画不出来。五项技术逐层拆掉这三堵墙:懒加载(只取可见格子)、表格切片(DOM 里只留可见行)、无限像素(滚动条按 downscale 缩放绕过元素高度上限)、像素精确滚动(全局跳转 + 局部微调双模式)、两步随机访问(键盘 / 跳行)。配一个可拨到 2 万亿行的真实视口,看 <tr> 数与已加载数据始终是常数。
🔗 相关链接
📐 行为 · 规范 / MDN
- CSS Overscroll Behavior Module Level 1 W3C / CSSWG overscroll-behavior 的规范本体(auto / contain / none / chain 与轴向、逻辑变体)。
- MDN · overscroll-behavior developer.mozilla.org 传播与边界效果的取值说明与轴向变体。
- Can I use · overscroll-behavior caniuse.com 实时浏览器支持矩阵。
-
Chrome 150 beta:overscroll-behavior: chain
developer.chrome.com
补齐第四个值
chain(传播但不回弹)的发布说明。 - CSS Overflow Module Level 3 · §4.1 scroll-behavior W3C / CSSWG scroll-behavior 的规范本体(由 CSSOM View 在程序滚动时遵循)。
- MDN · scroll-behavior developer.mozilla.org auto / smooth,以及它只影响程序滚动的范围界定。
- Can I use · scroll-behavior caniuse.com 实时浏览器支持矩阵。
-
MDN · Window.scrollTo()(返回 Promise)
developer.mozilla.org
返回值一节说明返回兑现
{ interrupted }的 Promise,含「滚动结束后淡入淡出」示例与特性检测。 - MDN · scrollend 事件 developer.mozilla.org 容器级的滚动结束事件,与「绑定单次调用」的 Promise 互补。
- CSS Overflow Module Level 5 · §Scroll axis locking W3C / CSSWG scroll-axis-lock 的规范本体(auto / none 两个值,阈值留给 UA)。
- scroll-axis-lock explainer github.com 动机、备选方案(为何不用 touch-action、为何不继承)与改名由来。
- Scroll Axis Lock nerdy.dev Adam Argyle 对首个实现的实测与手感评价,含两个可玩 demo。
- CSS Scroll Anchoring Module Level 1 W3C / CSSWG overflow-anchor 与锚节点选择算法的规范本体。
-
MDN · CSS scroll anchoring(overflow-anchor)
developer.mozilla.org
滚动锚定的工作原理与
overflow-anchor: none退出。 - Can I use · overflow-anchor(scroll anchoring) caniuse.com 实时浏览器支持矩阵(注意 Safari 长期缺位)。
- CSS Scroll Snap Module Level 1 W3C / CSSWG 吸附五属性的权威定义。
- MDN · Scroll snap(术语表) developer.mozilla.org snap-container / snapport / snap-area 的术语定义入口。
- Can I use · CSS Scroll Snap caniuse.com 实时浏览器支持矩阵。
- MDN · scrollsnapchange / scrollsnapchanging 事件 developer.mozilla.org 原生读出吸附目标的 SnapEvent(snapTargetBlock/Inline)。
- Carousels with CSS(scroll-marker / scroll-button) developer.chrome.com 纯 CSS 给 snap carousel 加圆点指示器与翻页按钮(Chrome 135+)。
- CSS Conditional Rules Module Level 5 W3C / CSSWG scroll-state() 容器查询(stuck / snapped / scrollable)的规范本体。
- Style an element relative to its scroll state developer.chrome.com scroll-state 容器查询(stuck / snapped / scrollable)的官方发布与示例(Chrome 133+)。
- MDN · @container(含 scroll-state()) developer.mozilla.org 容器查询总入口,scroll-state() 查询的语法定义所在。
🎚️ 外观 · 自定义滚动条
- MDN · ::-webkit-scrollbar developer.mozilla.org WebKit / Blink 伪元素族:thumb / track / button / corner 各自的命中与样式。
- MDN · scrollbar-color / scrollbar-width developer.mozilla.org CSS Scrollbars Styling Module 的标准换肤属性,跨浏览器的官方途径。
- Can I use · CSS scrollbar styling caniuse.com scrollbar-width / scrollbar-color 的实时浏览器支持矩阵。
- MDN · scrollbar-gutter developer.mozilla.org 为滚动条预留 gutter,消除内容在滚动条显隐时的横向抖动。
- 用 Canvas 造一个更好的滚动条 roriri.one 从零用 Canvas 重写滚动条:LERP 阻尼、ResizeObserver、移动端适配的实战细节。
- OverlayScrollbars(DOM 自绘库) kingsora.github.io 成熟的 DOM 自绘滚动条库,可对照「DOM 自绘」一档的工程化实现。
📦 海量数据 · 虚拟滚动
- Virtual Scrolling for Billions of Rows rednegra.net 本系列「虚拟滚动」页的缘起:逐层拆解 HighTable 的五项技术(懒加载 / 切片 / 无限像素 / 双模式滚动 / 两步随机访问)。
- HighTable(组件与在线 demo) hyparam.github.io 文章所述技术的开源实现,可直接拖动一个上亿行的真实表格。
- Complexities of an Infinite Scroller developer.chrome.com DOM 回收式无限滚动的经典讲解,与「表格切片」同源的思路。
- react-window(列表 / 表格虚拟化库) github.com 工程化的虚拟列表实现,可对照 overscan、可变行高等扩展。
🎮 玩一玩 / 灵感
- OH Snap — That Scroll Control oh-snap.netlify.app 把 scroll snap 各属性可视化讲透的交互站点。
- Scroll Snap 示例合集(CodePen) codepen.io 一组 scroll-snap 实战 pen,可直接动手体验。
- scroll-snap demo x181.cn 一个简洁的 scroll snap 演示页。
-
Complexities of an Infinite Scroller
developer.chrome.com
DOM 回收式无限滚动的实现——正是要
overflow-anchor: none自管滚动的典型场景。
scroll 反复计算来勉强实现(手动拦传播、手写补间动画、手算 scrollTop 防跳、手动吸附),既抖又难维护;如今浏览器把它们做成了声明式 CSS 属性,代码更短、手感更稳。而滚动条「外观」反过来——能换肤的标准属性有限,真要做复杂可视化滚动条,仍得退回 DOM / Canvas 自己接管。到了「海量数据」这一面则更靠工程而非单个 CSS 属性:虚拟滚动把渲染、加载与滚动条高度三件事各自从「随行数线性增长」压成常数。