如何将绿色Div置于红色Div下方并修复ScrollMagic固定定位失效问题
解决自定义平滑滚动与ScrollMagic.setPin冲突的问题
我完全懂你遇到的麻烦——启用基于transform的自定义平滑滚动后,ScrollMagic的setPin直接失效了。核心原因是ScrollMagic默认依赖原生滚动事件和页面scrollTop值来计算元素位置,但你的平滑滚动是通过修改容器的y偏移模拟出来的,原生scrollTop根本没同步变化,导致ScrollMagic完全摸不准固定元素该待在哪。
下面是完整的修复方案,同时满足红色div固定、绿蓝div在下方、全响应式的所有需求:
核心修复思路
- 让ScrollMagic改用你自定义的滚动值(也就是
scroller.y)来计算动画和固定状态 - 调整DOM结构和样式,确保元素层级、定位逻辑正确
- 保持平滑滚动的同时,实时同步滚动数据给ScrollMagic控制器
修改后的完整代码
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Test Code</title> <!-- 引入必要的依赖库 --> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.2/TweenMax.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.8/ScrollMagic.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.8/plugins/animation.gsap.min.js"></script> </head> <style> /* 全屏固定视图容器,作为滚动的视口 */ .viewport { overflow: hidden; position: fixed; height: 100vh; width: 100vw; top: 0; left: 0; margin: 0; } /* 滚动容器:平滑滚动的载体,元素自然垂直排列 */ .scroll-container { position: absolute; width: 100%; } /* 红色div:由ScrollMagic负责固定 */ .div1 { width: 70%; height: 70vh; border: 5px solid red; margin: 0 auto; } /* 绿蓝div:正常滚动的内容元素 */ .div2 { width: 70%; height: 70vh; border: 5px solid green; margin: 0 auto; } .div3 { width: 70%; height: 70vh; border: 5px solid blue; margin: 0 auto; } /* 清除默认边距,确保响应式布局正常 */ body { margin: 0; } </style> <body> <div class="viewport"> <div id="scroll-container"> <div class="div1"></div> <div class="div2"></div> <div class="div3"></div> </div> </div> </body> <script> var html = document.documentElement; var body = document.body; var scroller = { target: document.querySelector("#scroll-container"), ease: 0.05, // 平滑滚动速度 endY: 0, y: 0, resizeRequest: 1, scrollRequest: 0, }; var requestId = null; // 初始化ScrollMagic控制器,关键配置自定义滚动容器 var controller = new ScrollMagic.Controller({ container: scroller.target, // 告诉ScrollMagic监听我们的滚动容器 scrollTo: function(newPos) { window.scrollTo(0, newPos); } }); // 创建Pin场景:让红色div在滚动到绿色div之前保持固定 new ScrollMagic.Scene({ triggerElement: ".div2", // 触发点为绿色div顶部 triggerHook: 0, duration: "100%" // 固定状态持续到绿色div开始进入视口 }) .setPin(".div1") // 固定红色div .addTo(controller); TweenLite.set(scroller.target, { rotation: 0.01, force3D: true }); window.addEventListener("load", onLoad); function onLoad() { updateScroller(); window.focus(); window.addEventListener("resize", onResize); document.addEventListener("scroll", onScroll); } function updateScroller() { var resized = scroller.resizeRequest > 0; if (resized) { var height = scroller.target.clientHeight; body.style.height = height + "px"; scroller.resizeRequest = 0; controller.update(); // 窗口 resize 时同步更新ScrollMagic } var scrollY = window.pageYOffset || html.scrollTop || body.scrollTop || 0; scroller.endY = scrollY; scroller.y += (scrollY - scroller.y) * scroller.ease; if (Math.abs(scrollY - scroller.y) < 0.05 || resized) { scroller.y = scrollY; scroller.scrollRequest = 0; } TweenLite.set(scroller.target, { y: -scroller.y }); // 关键:把自定义滚动值同步给ScrollMagic,让它能正确计算固定状态 controller.scrollTo(scroller.y); requestId = scroller.scrollRequest > 0 ? requestAnimationFrame(updateScroller) : null; } function onScroll() { scroller.scrollRequest++; if (!requestId) { requestId = requestAnimationFrame(updateScroller); } } function onResize() { scroller.resizeRequest++; if (!requestId) { requestId = requestAnimationFrame(updateScroller); } } </script> </html>
关键改动细节
- DOM结构调整:把红色div移入
scroll-container,让ScrollMagic能正确识别它和绿蓝div的位置关系,自然实现上下排列 - 样式优化:移除红色div的
position: fixed(交给ScrollMagic的setPin处理),用margin: 0 auto实现水平居中,确保全屏幕响应式 - ScrollMagic核心配置:
- 初始化时指定
container: scroller.target,让它监听我们的自定义滚动容器 - 在
updateScroller函数中添加controller.scrollTo(scroller.y),实时同步滚动数据
- 初始化时指定
- Pin场景设置:以绿色div为触发点,让红色div在滚动到绿色div顶部时结束固定,完美匹配你的需求
这样修改后,红色div会在滚动到绿色div之前保持固定,绿蓝div正常在下方滚动,平滑滚动效果也完全保留,所有元素都能适配不同屏幕尺寸。
内容的提问来源于stack exchange,提问作者Sameer Khan
相关产品推荐
相关产品推荐

