You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

jQuery基于scroll实现元素translate滚动动画 跳滚错位问题求解

滚动层叠滑入效果错位问题修复方案

问题核心原因

原有逻辑只在单个元素对应的滚动区间内更新元素位移样式,区间外完全不处理元素状态:

  • 快速滚动、锚点跳转时会跳过中间的scroll事件触发点,无法走到对应元素的判断分支,元素样式不会更新
  • 正常滚动时只要离开某元素的触发区间,该元素的位移就停留在离开瞬间的值,不会自动归位到完全展开/完全收起的正确状态

修复逻辑

每次滚动触发时遍历所有滑入元素,给每个元素明确三个状态的位移规则,不存在样式更新遗漏:

  1. 未进入触发区间:元素固定在初始位置,完全隐藏在视口下方
  2. 处于触发区间内:位移值随滚动距离线性变化,实现跟手滑入效果
  3. 已滑过触发区间:元素固定在完全展开位置,覆盖下层内容
    额外增加初始化状态校验、窗口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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 19:01:46