滚动吸附(Scroll Snap)
你见过商品页的卡片图吗?每次切换卡片自动居中,现在已经可以通过 CSS 实现了,无需 JavaScript。
CSS 滚动吸附模块可以定义用户在滚动浏览文档过程中,使滚动子元素自动对齐可视区域的某些位置。例如:多张卡片预览元素滚动自动居中。
又比如,产品官网首页对产品特点描述时,往往采用卡片式的介绍,而这些卡片通常近似整个屏幕大小,每一张卡片通过向下滑动的方式切换。例如第一张卡片介绍产品的用户定位,当鼠标滚轮向下滚动时,第二张介绍卡片展示商品拥有哪些优势。同时结合容器状态查询和滚动驱动动画,在卡片之间切换时可以有些炫酷的切入动画。请注意,我没有提到任何有关 ECMAScript 的事情。
参考资料
前言
滚动吸附的效果首先得有滚动的容器才能吸附,因此一定具备一个可以滚动的容器,容器才好捕获滚动的元素。滚动吸附模块分为对容器和对子元素的属性。
必须设置的属性
要实现吸附效果,至少需要包含 3 个属性:
| 属性 | 作用 | 应用于 |
|---|---|---|
overflow | 有滚动才有吸附 | 容器 |
scroll-snap-type | 声明吸附的轴和吸附严格度 | 容器 |
scroll-snap-align | 子元素如何对齐容器可视区域的吸附位置 | 子元素 |
针对滚动容器
滚动吸附模块针对滚动容器的属性包括:
overflow:必须将此属性设置为可以滚动scroll-snap-type:使用此属性决定是否开启子元素吸附。此属性包含吸附的轴和吸附元素时的宽容度scroll-padding:声明容器在吸附子元素时预留的内边距
针对容器子元素
针对容器子元素的属性包括:
scroll-snap-align:定义滚动容器子元素吸附在滚动轴可视区域的开始、中间还是结束位置scroll-snap-stop:定义滚动容器的子元素是否每次滚动强制停留到自身。这是出于用户有可能快速滚动时忽略了中间的重要信息。如果设置为always则表示每张卡片强制停留并预览scroll-margin:子元素在距离滚动容器内可视区域边界的外边距
实例详解
大多数元素在官方都有案例提供,但还是不够丰富。我将针对一些不太容易明显体会出区别的属性做出详细案例和说明。
scroll-snap-type
语法:
scroll-snap-type: <x | y | block | inline | both> <mandatory | proximity>;
scroll-snap-type 属性需要知道在哪个方向上有滚动吸附。方向可以是 x、y 或者逻辑对应关系 block、inline,还可以用关键字 both 使两个轴都有滚动吸附。
此属性除了可以声明吸附的轴以外,还可以说明吸附的宽松程度,通常放在第二个参数上:
mandatory:声明了子元素被捕获点捕获proximity(默认值):和mandatory区别在于,子元素是否被其设置的捕获点对齐取决于浏览器的实现。通常情况下,该值的默认行为是:如果一个滚动元素的边框没有位于捕获点内时,它就不会被捕获对齐
HTML 示例:
<section
class="snap-type-page__scroller snap-type-page__scroller--mandatory"
title="snap-mandatory"
>
<div><span>100px</span></div>
<div><span>100px</span></div>
<div><span>100px</span></div>
</section>
<section
class="snap-type-page__scroller snap-type-page__scroller--proximity"
title="snap-proximity"
>
<div><span>100px</span></div>
<div><span>100px</span></div>
<div><span>100px</span></div>
</section>
CSS 示例:
这里针对两个不同捕获严格程度的 <section>,通过 CSS 对它们进行不同的声明。这里必须设置 overflow,否则滚动吸附就没有意义。
.snap-type-page__scroller {
display: flex;
flex-direction: column;
gap: 2.5rem;
overflow-y: auto;
}
/* 第一个参数 y:纵向吸附;第二个参数 mandatory — 滚动结束必须落在吸附点 */
.snap-type-page__scroller--mandatory {
scroll-snap-type: y mandatory;
}
/* 第二个参数 proximity — 仅当停止位置靠近吸附点时才对齐 */
.snap-type-page__scroller--proximity {
scroll-snap-type: y proximity;
}
如下案例,灰色的虚线标示所有子元素捕获的辅助线。可以将元素块与捕获辅助线保持距离,就能体会到具体的区别。
注意:假设强制声明子元素必须对齐捕获点(也就是设置
mandatory参数),需要避免以下问题:假设某个子元素非常大,如果严格按照吸附位置严格对齐,那么将会导致超出滚动可视区域部分不可见。当用户想滚动去查看超出可视区域的内容时,因为强制捕获对齐的关系,用户滚动的位置又会被吸附获取,造成内容再次不可见,会对用户造成很大的困扰。此属性只有确保你的滚动子元素不会在捕获点产生溢出时使用,以增强对交互效果的可预测性。
scroll-snap-align
此属性用于标明:子元素如何对齐父容器滚动轴的可视区域位置。有效值包括 start、end、center 和 none。
HTML 示例:
<section class="snap-align-start">
<div><span>start</span></div>
<div><span>start</span></div>
<div><span>start</span></div>
</section>
<section class="snap-align-center">
<div><span>center</span></div>
<div><span>center</span></div>
<div><span>center</span></div>
</section>
<section class="snap-align-end">
<div><span>end</span></div>
<div><span>end</span></div>
<div><span>end</span></div>
</section>
CSS 示例:
在下列 CSS 示例中,给不同的 <section> 设置不同的 scroll-snap-align 值,以查看不同的对齐位置。
section {
height: 100px;
overflow-y: auto;
scroll-snap-type: y mandatory;
}
.snap-align-start > div {
scroll-snap-align: start;
}
.snap-align-center > div {
scroll-snap-align: center;
}
.snap-align-end > div {
scroll-snap-align: end;
}
可以滚动下列不同的滚动条,体会不同 scroll-snap-align 的值的不同对齐行为。
特性前瞻
注意:现在(2026 年 3 月 22 日),对于滚动所附加的伪元素部分,还未被主流浏览器所支持,请谨慎使用。
::scroll-button:滚动按钮元素::scroll-marker:滚动元素的标记::scroll-marker-group:所有滚动元素的标记容器