带粘性元素的滚动动画实现求助
实现滚动驱动的图片与双列文本联动效果
核心思路
通过监听滚动事件,分三个阶段控制元素的定位与位移:
- 初始阶段:图片覆盖视口,左文本列固定在视口顶部(被图片遮挡)
- 滚动初期:图片随滚动向上移动,逐渐露出固定的左文本列
- 滚动中期:图片锁定在顶部,右文本列向上移动至原始位置
- 滚动后期:所有元素恢复正常文档流滚动
完整实现代码
HTML
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.0.2/css/bootstrap.min.css" rel="stylesheet" /> <section class="section"> <div class="container"> <div class="row image-container"> <div class="col-12"> <img src="https://placehold.co/1200x600" alt="Hero Image" /> </div> </div> <div class="row wrapper"> <div class="col-6 text text-left"> <h2>TEXT 1</h2> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus porttitor purus sit amet dapibus eleifend. Sed vel erat quis leo vehicula ultrices. Pellentesque augue ipsum, molestie sed quam at, vestibulum viverra nisi. Nunc efficitur lorem sed lacus pellentesque facilisis. Nullam commodo, sapien ultrices dapibus interdum, lorem quam dictum risus, a commodo purus nisl quis mi. Integer vestibulum aliquet porttitor. Vestibulum auctor neque vitae ipsum varius dignissim. Nunc quis augue vel sapien auctor ultricies. Maecenas varius libero iaculis neque placerat, sit amet hendrerit purus condimentum. Aenean fringilla eros ac interdum commodo. Morbi scelerisque lorem ut nisl ornare, mattis efficitur sem aliquam. In hendrerit tempus lectus, eu tincidunt tellus volutpat in.</p> </div> <div class="col-6 text text-right"> <h2>TEXT 2</h2> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus porttitor purus sit amet dapibus eleifend. Sed vel erat quis leo vehicula ultrices. Pellentesque augue ipsum, molestie sed quam at, vestibulum viverra nisi. Nunc efficitur lorem sed lacus pellentesque facilisis. Nullam commodo, sapien ultrices dapibus interdum, lorem quam dictum risus, a commodo purus nisl quis mi. Integer vestibulum aliquet porttitor. Vestibulum auctor neque vitae ipsum varius dignissim. Nunc quis augue vel sapien auctor ultricies. Maecenas varius libero iaculis neque placerat, sit amet hendrerit purus condimentum. Aenean fringilla eros ac interdum commodo. Morbi scelerisque lorem ut nisl ornare, mattis efficitur sem aliquam. In hendrerit tempus lectus, eu tincidunt tellus volutpat in.</p> <p>Aenean ac nunc rutrum, sagittis dui pellentesque, malesuada lorem. Sed sed bibendum lorem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam eu neque neque. Fusce quis enim interdum, efficitur arcu at, dignissim turpis. Quisque bibendum risus nec tempus congue. Ut at libero ut ipsum venenatis dignissim id non purus. Vestibulum et bibendum dui, a sollicitudin lectus. Morbi mollis auctor risus at sodales.</p> <p>Nullam elit velit, consectetur et pretium eget, dapibus in odio. Integer nec dignissim massa. Duis convallis tellus eu molestie ornare. Quisque dapibus ipsum et euismod laoreet. Suspendisse vulputate dui quis lacinia vehicula. Integer ac commodo ligula. Pellentesque at vehicula ligula. Mauris nec luctus nunc, id pharetra mauris. Aenean rhoncus orci quam, sit amet elementum tortor lobortis quis. Ut in scelerisque nisl.</p> <p>Nullam elit velit, consectetur et pretium eget, dapibus in odio. Integer nec dignissim massa. Duis convallis tellus eu molestie ornare. Quisque dapibus ipsum et euismod laoreet. Suspendisse vulputate dui quis lacinia vehicula. Integer ac commodo ligula. Pellentesque at vehicula ligula. Mauris nec luctus nunc, id pharetra mauris. Aenean rhoncus orci quam, sit amet elementum tortor lobortis quis. Ut in scelerisque nisl.</p> </div> </div> </div> </section>
CSS
.section { padding: 50px 0; /* 增加底部内边距,确保滚动有足够空间 */ padding-bottom: 100vh; } img { max-width: 100%; } .image-container { position: relative; z-index: 2; /* 初始让图片容器占满视口高度,确保覆盖文本 */ min-height: 100vh; display: flex; align-items: center; } .wrapper { position: relative; margin-top: -600px; /* 与图片高度一致,让文本列与图片重叠 */ z-index: 1; } .text-left { background: lightyellow; position: fixed; top: 0; left: calc(50% - 570px); /* 基于Bootstrap容器宽度计算,适配1200px容器 */ width: 570px; max-height: 100vh; overflow-y: auto; /* 初始隐藏,通过滚动逐渐露出 */ opacity: 0; } .text-right { background: lightgreen; position: relative; } /* 滚动完成后恢复正常文档流 */ .section.scrolled .image-container { position: static; min-height: auto; } .section.scrolled .text-left { position: static; left: auto; width: auto; max-height: none; opacity: 1; } .section.scrolled .wrapper { margin-top: 20px; }
JavaScript
const section = document.querySelector('.section'); const imageContainer = document.querySelector('.image-container'); const textLeft = document.querySelector('.text-left'); const textRight = document.querySelector('.text-right'); const imgHeight = 600; // 图片高度 const leftTextHeight = textLeft.offsetHeight; const rightTextHeight = textRight.offsetHeight; // 计算各阶段滚动阈值 const phase1End = imgHeight; const phase2End = phase1End + (rightTextHeight - window.innerHeight); window.addEventListener('scroll', () => { const scrollY = window.scrollY; // 阶段1:图片随滚动向上移动,露出左文本 if (scrollY <= phase1End) { imageContainer.style.transform = `translateY(-${scrollY}px)`; // 逐渐显示左文本 textLeft.style.opacity = scrollY / phase1End; section.classList.remove('scrolled'); } // 阶段2:图片锁定,右文本向上移动 else if (scrollY > phase1End && scrollY <= phase2End) { imageContainer.style.transform = `translateY(-${phase1End}px)`; textLeft.style.opacity = 1; textRight.style.transform = `translateY(-${scrollY - phase1End}px)`; section.classList.remove('scrolled'); } // 阶段3:恢复正常滚动 else { imageContainer.style.transform = ''; textRight.style.transform = ''; section.classList.add('scrolled'); } }); // 初始化位置调整 window.dispatchEvent(new Event('scroll'));
关键细节说明
- 初始重叠布局:通过
.wrapper的负margin让文本列与图片容器重叠,实现初始图片覆盖文本的效果 - 固定左文本列:
.text-left初始设置为fixed定位,通过滚动距离控制透明度,实现逐渐露出的效果 - 分阶段滚动控制:通过计算滚动阈值,分别控制图片的位移、右文本的位移,最后恢复正常文档流
- 性能优化:使用
transform而非top/margin控制位移,避免重排提升流畅度
内容的提问来源于stack exchange,提问作者Matteor
相关产品推荐
相关产品推荐

