← 首页 / 📜 Scroll · 滚动怎么动、长什么样、装得下多少 待审核 9 页

📜 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 · 传播与回弹

overscroll-behavior:滚到底之后,溢出的滚动去哪

一个属性同时管两件独立的事:滚动传播(嵌套容器滚到头,多余滚动量是否「漏」给祖先、带动整页)与边界默认效果(下拉刷新 / 橡皮筋回弹)。auto / contain / none 加上 Chrome 150 新增的 chain,四个关键字正好填满一张 2×2 表——动手玩嵌套滚动,看 contain 如何把滚动关在弹层里。

scroll-axis-lock · 轴锁

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

触屏与触控板上斜向拖动,浏览器会把手势投影到位移较大的那条轴,另一维分量整段丢弃,路径走成楼梯。这条启发式挡的是阅读流里的误漂,代价落在地图与无限画布这类二维界面上。scroll-axis-lock: none 关掉它。

scroll-behavior · 平滑

scroll-behavior:让跳转滚动从瞬移变平滑

点目录跳转、scrollIntoView()、改 #hash 这类程序触发的滚动默认瞬移,scroll-behavior: smooth 给它加平滑补间。动手体验,看 CSS 默认值与 JS 逐次 behavior 选项谁覆盖谁,以及为何要为 prefers-reduced-motion 退回瞬时。

scrollTo() Promise · interrupted

scrollTo() 返回 Promise:等程序滚动停下再动手

scroll-behavior 让程序滚动变平滑,却没告诉你它何时到位。Chrome / Edge 150 起 scrollTo() / scrollBy() / scroll() 返回一个 Promise,滚动停下时兑现 { interrupted }——「滚到位再淡入某块内容」这类衔接动画可直接 await 出来。动手看平滑滚动结束后工具条淡入,以及快速二次点击时 interrupted: true 的打断,附特性检测与 scrollend 降级。

overflow-anchor · 锚定

overflow-anchor:上方内容变高时别让画面乱跳

图片加载 / 内容注入让上方变高,你正看的行突然下跳——scroll anchoring 是浏览器的默认补偿,悄悄调 scrollTop 把你看的内容留在原地。动手在顶部插内容,对比 auto(补偿)与 none(退出,把控制权交给无限滚动 / 聊天那种自管滚动的逻辑)。

scroll snap · 吸附

CSS Scroll Snap:让滚动整齐地停靠

carousel / 整屏分页希望松手后停在整齐位置。围绕三个角色(snap-container / snapport / snap-area)的五个属性——scroll-snap-type / scroll-padding / scroll-snap-align / scroll-margin / scroll-snap-stop——拖一个横向 carousel,看 mandatoryproximity 的手感差别。

scroll-state · 容器查询

scroll-state:让样式读出容器此刻的滚动态

container-type: scroll-state 把容器的滚动态暴露给样式层,后代用 @container scroll-state(...) 按态换样式,零脚本。三种查询各对应一个常见需求:stuck(sticky 贴住边没有)做贴顶变身、snapped(是不是当前吸附的那个)给 carousel 点亮、scrollable(某方向还有没有可滚内容)做边缘渐隐提示——三个可交互 demo + 渐进增强降级。

滚动条长什么样 · 外观

另一面是滚动条本身的样子。从 CSS 换肤到 DOM、Canvas 自绘,三档递进地把滚动条的外观与交互逐步接管。

::-webkit-scrollbar · DOM · Canvas

三种自定义滚动条的方式

换皮肤 / 接管交互 / 整条自绘三档递进:::-webkit-scrollbar 与标准 scrollbar-color 只改外观;DOM 隐藏原生用 div 自绘 track / thumb;Canvas 整条画在画布上配 LERP 阻尼。三个 demo 滚同一段文本对比手感。

滚动装得下多少 · 海量数据

前两面都还在样式与交互行为的范畴。第三面是工程问题:当数据本身大到十亿、万亿行,一次性渲染与加载都会崩,虚拟滚动如何用切片、懒加载与滚动条缩放,只靠原生能力把它滚动起来。

virtual scroll · table slice · infinite pixels

虚拟滚动:在浏览器里滚动十亿行

数据大到十亿、万亿行时,全量渲染撑爆 DOM、全量加载撑爆内存、连一根够高的滚动条都画不出来。五项技术逐层拆掉这三堵墙:懒加载(只取可见格子)、表格切片(DOM 里只留可见行)、无限像素(滚动条按 downscale 缩放绕过元素高度上限)、像素精确滚动(全局跳转 + 局部微调双模式)、两步随机访问(键盘 / 跳行)。配一个可拨到 2 万亿行的真实视口,看 <tr> 数与已加载数据始终是常数。

🔗 相关链接

📐 行为 · 规范 / MDN

🎚️ 外观 · 自定义滚动条

📦 海量数据 · 虚拟滚动

🎮 玩一玩 / 灵感

为什么值得专门认识它们: 「行为」这四件事过去多半要靠 JS 监听 scroll 反复计算来勉强实现(手动拦传播、手写补间动画、手算 scrollTop 防跳、手动吸附),既抖又难维护;如今浏览器把它们做成了声明式 CSS 属性,代码更短、手感更稳。而滚动条「外观」反过来——能换肤的标准属性有限,真要做复杂可视化滚动条,仍得退回 DOM / Canvas 自己接管。到了「海量数据」这一面则更靠工程而非单个 CSS 属性:虚拟滚动把渲染、加载与滚动条高度三件事各自从「随行数线性增长」压成常数。