粘性定位入场、非粘性退场(无JS):多sticky元素同步退场实现方案问询
实现多个sticky元素同步被推出屏幕的方案
这个需求确实没法用纯原生position:sticky的默认行为实现,因为每个sticky元素的“粘性脱离点”是独立的——当元素的top偏移加上自身高度触达父容器底部时,就会开始被滚动推出。要让它们同步,我们需要把它们的位置变化绑定到同一个滚动进度上,下面给你两个可行的方案:
方案一:CSS滚动驱动动画(现代浏览器推荐)
这个方案利用CSS的scroll-timeline特性,把三个元素的动画绑定到主容器的滚动进度上,实现同步位移,完全不需要JS:
修改后的CSS代码
html,body{width:100%;height:100%;margin:0} main{width:57.5vh;height:375vh;position:absolute;left:50%;transform:translateX(-50%);font-family:arial black;color:#fff;background:gold; /* 定义滚动时间线,绑定main元素的滚动 */ scroll-timeline: --main-scroll block; } .title{--height:25vh;height:var(--height);text-align:center;line-height:var(--height)} .e{background:pink} .o{background:skyblue} main>div:nth-child(1){height:100vh} main>div:nth-child(2){z-index:5;top:0;position:sticky} /* STEP: 1 */ main>div:nth-child(3){height:75vh} /* 调整父容器为relative,让内部的固定容器基于它定位 */ main>div:nth-child(4){height:150vh;position:relative;border:solid red 2px;box-sizing:border-box;} /* 新增粘性容器,作为三个STEP元素的定位基准 */ .sticky-group{ position: sticky; top: 0; height: 100%; position: relative; } /* 给三个STEP元素添加滚动驱动动画 */ .sticky-step{ position: absolute; left: 0; width: 100%; animation: sticky-slide linear forwards; animation-timeline: --main-scroll; /* 动画触发范围:根据你的布局计算,从滚动到STEP4即将被推出的位置,到完全推出的位置 */ animation-range: 400vh 550vh; } .sticky-step:nth-child(1){top:25%} .sticky-step:nth-child(2){top:50%} .sticky-step:nth-child(3){top:75%} /* 定义同步推出的动画 */ @keyframes sticky-slide { to { transform: translateY(100vh); /* 把元素整体向下推出视口 */ } } .gap{height:100vh;background:limegreen}
修改后的HTML代码
<main> <div></div> <div class='title e'>STEP: 1</div> <div></div> <div> <div class="sticky-group"> <div class='title o sticky-step'>STEP: 2</div> <div class='title e sticky-step'>STEP: 3</div> <div class='title o sticky-step'>STEP: 4</div> </div> <div></div> <div></div> <div></div> <div class='gap'>GAP</div> </div> </main>
原理说明
我们把三个STEP元素从原来的流式布局中抽离,放到一个sticky的容器里,用绝对定位固定它们的初始位置。然后通过scroll-timeline绑定主容器的滚动进度,当滚动到指定范围时,触发同步的translateY动画,让三个元素一起被推出屏幕。
方案二:JavaScript滚动监听(兼容旧浏览器)
如果需要兼容不支持scroll-timeline的浏览器,可以用JS监听滚动事件,动态计算滚动进度来控制元素的位移:
修改后的CSS代码
html,body{width:100%;height:100%;margin:0} main{width:57.5vh;height:375vh;position:absolute;left:50%;transform:translateX(-50%);font-family:arial black;color:#fff;background:gold;overflow-x:hidden} .title{--height:25vh;height:var(--height);text-align:center;line-height:var(--height)} .e{background:pink} .o{background:skyblue} main>div:nth-child(1){height:100vh} main>div:nth-child(2){z-index:5;top:0;position:sticky} /* STEP: 1 */ main>div:nth-child(3){height:75vh} main>div:nth-child(4){height:150vh;position:relative;border:solid red 2px;box-sizing:border-box;} .sticky-group{ position: sticky; top: 0; height: 100%; position: relative; } .sticky-step{ position: absolute; left: 0; width: 100%; transition: transform 0.1s ease; /* 让位移更平滑 */ } .sticky-step:nth-child(1){top:25%} .sticky-step:nth-child(2){top:50%} .sticky-step:nth-child(3){top:75%} .gap{height:100vh;background:limegreen}
JavaScript代码
const main = document.querySelector('main'); const stickySteps = document.querySelectorAll('.sticky-step'); // 计算触发同步推出的滚动位置范围(单位vh) const triggerStart = 100 + 25 + 75 + 150 - 100; // 前面元素的总高度减去视口高度,即STEP开始要被推出的位置 const triggerEnd = triggerStart + 100; // 滚动100vh后完全推出 main.addEventListener('scroll', () => { const scrollTop = main.scrollTop; const vh = window.innerHeight * 0.01; // 1vh对应的像素值 const startPos = triggerStart * vh; const endPos = triggerEnd * vh; if (scrollTop >= startPos && scrollTop <= endPos) { // 计算当前滚动进度(0到1) const progress = (scrollTop - startPos) / (endPos - startPos); // 给所有STEP元素应用同步位移 stickySteps.forEach(step => { step.style.transform = `translateY(${progress * 100}vh)`; }); } else if (scrollTop > endPos) { // 完全推出屏幕 stickySteps.forEach(step => { step.style.transform = `translateY(100vh)`; }); } else { // 回到初始位置 stickySteps.forEach(step => { step.style.transform = `translateY(0)`; }); } });
原理说明
通过监听主容器的滚动事件,计算当前滚动位置是否在触发范围内,然后根据滚动进度动态设置所有STEP元素的transform属性,让它们同步向下移动,模拟“一起被推出”的效果。
为什么原方案无法实现?
原方案中每个sticky元素的粘性上下文是它们的父容器,当父容器滚动时,每个元素会在自身的top偏移加上高度触达父容器底部时,开始脱离粘性状态被推出——因为它们的top值不同,所以触发时机也不同,自然会逐个被推出。我们的方案是脱离原生sticky的独立触发逻辑,改用统一的滚动进度来控制位置,从而实现同步效果。
内容的提问来源于stack exchange,提问作者oldboy
相关产品推荐
相关产品推荐

