滚动sticky定位Div时文本动画异常及优化需求技术问询
解决Sticky容器滚动时标题动画触发问题
需求与问题
需求
- 实现
position: sticky容器,滚动时依次为四个标题切换.active类展示动画 - 最终保留最后一个标题可见,直到滚动到末尾容器
遇到的问题
- Sticky容器变为粘性状态后,滚动事件无法正确检测后续滚动以触发标题切换
- 尝试仅在Sticky容器粘性状态时启动动画,导致滚动动画完全失效
- 希望所有标题处于同一区块,但设置
position: absolute会破坏动画效果
用户原始代码
HTML
<section class='hero'> <p>Start</p> </section> <section class='sticky'> <div class='text-animations'> <h1 class='animated-text active'>Intro</h1> <h2 class='animated-text'>First</h2> <h2 class='animated-text'>Second</h2> <h2 class='animated-text'>Third</h2> </div> <p class='intro_content'> Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam </p> </section> <section class='end'> <p>End</p> </section>
CSS
body { margin: 0 } section { position: relative; } .sticky { padding-bottom: 150px; background: #2d232c; position: sticky; top: 0; overflow: hidden; height: auto; color: white; } .animated-text { opacity: 0; height: 0; overflow: hidden; transition: opacity 1s ease, height 1s ease, transform 1s ease; transform: translateY(0); } .animated-text.active { height: auto; opacity: 1; transform: translateY(-20px); } .hero, .end { height: 100px; background: white; }
JavaScript
const headings = Array.from(document.querySelectorAll('.animated-text')); const sticky = document.querySelector('.sticky'); let currentActive = null; window.addEventListener('scroll', () => { const viewportHeight = window.innerHeight; headings.forEach((heading, index) => { const headingRect = heading.getBoundingClientRect(); if (headingRect.top <= viewportHeight / 2) { if (currentActive) { currentActive.classList.remove('active'); } heading.classList.add('active'); currentActive = heading; } }); });
解决方案
核心思路
问题根源在于Sticky容器固定后,标题的视口位置不再随滚动变化,因此需要通过容器内的滚动进度而非标题自身位置来触发动画,同时通过绝对定位实现标题在同一区块的重叠切换。
修改后完整代码
HTML(无需修改)
<section class='hero'> <p>Start</p> </section> <section class='sticky'> <div class='text-animations'> <h1 class='animated-text active'>Intro</h1> <h2 class='animated-text'>First</h2> <h2 class='animated-text'>Second</h2> <h2 class='animated-text'>Third</h2> </div> <p class='intro_content'> Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam </p> </section> <section class='end'> <p>End</p> </section>
CSS
body { margin: 0 } section { position: relative; } .sticky { padding-bottom: 150px; background: #2d232c; position: sticky; top: 0; overflow: visible; /* 取消溢出隐藏,确保标题动画正常显示 */ min-height: 100vh; /* 初始占满视口,触发sticky后内容可滚动 */ color: white; } .text-animations { position: relative; /* 作为标题的定位父容器 */ height: 80px; /* 固定高度,让标题重叠在同一区块 */ } .animated-text { position: absolute; /* 绝对定位实现标题重叠 */ top: 0; left: 0; margin: 0; opacity: 0; transform: translateY(20px); transition: opacity 0.8s ease, transform 0.8s ease; } .animated-text.active { opacity: 1; transform: translateY(0); } .hero, .end { height: 100vh; /* 改为满屏,方便测试滚动流程 */ background: white; display: flex; align-items: center; justify-content: center; } .intro_content { margin-top: 100px; /* 拉开与标题的距离,提供滚动空间 */ padding: 0 20px; }
JavaScript
const headings = Array.from(document.querySelectorAll('.animated-text')); const sticky = document.querySelector('.sticky'); let currentActiveIndex = 0; // 预计算每个标题的滚动触发阈值 const triggerPoints = headings.map((_, index) => { return (sticky.scrollHeight - window.innerHeight) / headings.length * (index + 1); }); window.addEventListener('scroll', () => { // 判断Sticky容器是否处于粘性状态 const isSticky = sticky.offsetTop <= window.scrollY && window.scrollY <= sticky.offsetTop + sticky.scrollHeight - window.innerHeight; if (!isSticky) { // 非粘性状态时,重置为第一个标题激活 if (currentActiveIndex !== 0) { headings[currentActiveIndex].classList.remove('active'); headings[0].classList.add('active'); currentActiveIndex = 0; } return; } // 计算容器内的滚动进度 const scrollProgress = window.scrollY - sticky.offsetTop; // 根据滚动进度切换标题 headings.forEach((heading, index) => { if ((index === 0 || scrollProgress >= triggerPoints[index - 1]) && currentActiveIndex !== index) { headings[currentActiveIndex].classList.remove('active'); heading.classList.add('active'); currentActiveIndex = index; } }); });
关键修改说明
CSS调整:
- 给
.text-animations设置固定高度,标题用绝对定位重叠,实现同一区块切换且不破坏动画 - 将
.sticky的overflow改为visible,确保标题动画正常显示 - 调整首尾容器为满屏,优化滚动测试体验
- 给
JavaScript优化:
- 先判断Sticky容器是否处于粘性状态,仅在该状态下触发标题切换
- 通过计算容器内的滚动进度替代标题视口位置检测,解决Sticky固定后位置不更新的问题
- 预计算触发阈值,确保滚动时平滑依次切换标题
内容的提问来源于stack exchange,提问作者Álvaro
相关产品推荐
相关产品推荐

