jQuery基于scroll实现元素translate滚动动画 跳滚错位问题求解
滚动层叠滑入效果错位问题修复方案
问题核心原因
原有逻辑只在单个元素对应的滚动区间内更新元素位移样式,区间外完全不处理元素状态:
- 快速滚动、锚点跳转时会跳过中间的scroll事件触发点,无法走到对应元素的判断分支,元素样式不会更新
- 正常滚动时只要离开某元素的触发区间,该元素的位移就停留在离开瞬间的值,不会自动归位到完全展开/完全收起的正确状态
修复逻辑
每次滚动触发时遍历所有滑入元素,给每个元素明确三个状态的位移规则,不存在样式更新遗漏:
- 未进入触发区间:元素固定在初始位置,完全隐藏在视口下方
- 处于触发区间内:位移值随滚动距离线性变化,实现跟手滑入效果
- 已滑过触发区间:元素固定在完全展开位置,覆盖下层内容
额外增加初始化状态校验、窗口resize重算、滚动节流逻辑,覆盖刷新停在中间位置、窗口大小变化等边界场景。
修复后完整代码
JS 逻辑
// 全局尺寸计算 function calcLayoutSize() { let totalDocHeight = 0; $(".classic, .holder, .holder__item").each(function () { totalDocHeight += $(this).height(); }); $("body").height(totalDocHeight); window.prevSectionHeight = $(".classic").height(); window.windowHeight = $(window).height(); } // 滚动状态更新 function updateSlidePosition() { const currentScroll = $(window).scrollTop(); // 逐个计算所有滑入项的位置,无遗漏 $(".holder__item").each(function (index) { const $item = $(this); const offsetGap = 80 - index * 20; // 对应每个卡片的顶部边距差 const rangeStart = prevSectionHeight + windowHeight * index; const rangeEnd = prevSectionHeight + windowHeight * (index + 1); let translateY = 0; if (currentScroll < rangeStart) { // 未进入触发区:完全隐藏在下方 translateY = windowHeight - offsetGap; } else if (currentScroll >= rangeStart && currentScroll < rangeEnd) { // 触发区间内:跟手位移 translateY = windowHeight - offsetGap - (currentScroll - rangeStart); } else { // 已滑过区间:完全展开归位 translateY = 0; } $item.css({ transform: `translateY(-${translateY}px)` }) }) } $(function () { calcLayoutSize(); // 初始化就执行一次,避免刷新页面停在中间位置时错位 updateSlidePosition(); // 滚动事件绑定requestAnimationFrame节流,匹配屏幕刷新率优化性能 let ticking = false; $(window).on('scroll', function () { if (!ticking) { window.requestAnimationFrame(function () { updateSlidePosition(); ticking = false; }); ticking = true; } }) // 窗口尺寸变化时重新计算布局 $(window).on('resize', function () { calcLayoutSize(); updateSlidePosition(); }) });
CSS 样式
*, *::before, *::after { box-sizing: border-box; } * { margin: 0; } html, body { height: 100%; scroll-behavior: smooth; } section { height: 100vh; } .classic { background-color: pink; } .holder { text-align: center; display: flex; justify-content: center; align-items: center; position: sticky; top: 0px; overflow: hidden; } .holder h2 { font-size: 6rem; } .holder__item { height: 100vh; width: 100vw; position: absolute; left: 0; } .holder__item.item1 { height: calc(100vh - 80px); top: calc(100vh - 80px); border: 1px solid black; background-color: teal; } .holder__item.item2 { height: calc(100vh - 60px); top: calc(100vh - 60px); border: 1px solid red; background-color: crimson; } .holder__item.item3 { height: calc(100vh - 40px); top: calc(100vh - 40px); border: 1px solid purple; background-color: DarkSalmon; } .holder__item.item4 { height: calc(100vh - 20px); top: calc(100vh - 20px); background-color: tomato; border: 1px solid blue; }
HTML 结构
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <section class="classic"> </section> <section class="holder"> <div class="holder__placeholder"> <h2>Hello world.</h2> <p>Nice to see you again.</p> </div> <div class="holder__item item1"> <p>hello hello</p> </div> <div class="holder__item item2"></div> <div class="holder__item item3"></div> <div class="holder__item item4"></div> </section>
优化方向建议
- 可以移除jQuery依赖,用原生JS结合
scrollTop、requestAnimationFrame实现,减少资源体积 - 更复杂的层叠动效可以将滚动进度映射为CSS变量,减少JS直接操作样式的性能开销
- 位移计算可以改用比例值替代固定像素,适配不同尺寸屏幕时稳定性更高
内容的提问来源于stack exchange,提问作者Dendenzilla
相关产品推荐
相关产品推荐

