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

为何window.requestAnimationFrame无法保留上一次过渡状态?

解决requestAnimationFrame实现滑块时transform3d重置的问题

问题现象

开发类似fullpage.js的滑块组件时,使用window.requestAnimationFrame实现页面过渡动画,每次切换幻灯片时,动画都会从第一页(translate3d(0,0,0))开始,而非从当前页面位置平滑过渡到目标页面。直接赋值transform属性时功能正常,但结合requestAnimationFrame就出现位置重置问题。

问题根源

当前代码的核心问题在于:

  1. 动画计算的是目标页面的绝对偏移量(比如第二页是-100vh),用delta * offset直接从0开始过渡,忽略了滑块当前的实际位置
  2. 未处理动画过程中的滚轮事件拦截,可能导致多个动画叠加,进一步打乱位置状态

解决方案

通过记录当前实际偏移量、计算位置差值、增加动画锁三个核心修改,修复动画重置问题:

修改后的完整代码

HTML(保持不变)
<div class="scroller">
   <div class="scroller-content">
      <section class="scroller-section s1">SLIDE 1</section>
      <section class="scroller-section s2">SLIDE 2</section>
      <section class="scroller-section s3">SLIDE 3</section>
      <section class="scroller-section s4">SLIDE 4</section>
   </div>
</div>
CSS(保持不变)
.scroller {
  width: 100vw;
  height: 100vh;
  overflow-y: hidden;
}

.scroller-section {
  width: 100vw;
  height: 100vh;
  display: flex;
  text-align: center;
  align-items: center;
  justify-content: center;
  font-size: 25rem;
}

.scroller-section.s1 {
  background-color: #535bf2;
}
.scroller-section.s2 {
  background-color: #242424;
}

.scroller-section.s3 {
  background-color: #213547;
}

.scroller-section.s4 {
  background-color: #747bff;
}
JavaScript(核心修改)
const scrollerContent = document.querySelector(".scroller-content")
const offsets = [0, -100, -200, -300] // 各页面的目标偏移值(单位vh)
let currentIndex = 0
let currentOffset = 0 // 实时记录滑块当前的偏移量
let isAnimating = false // 动画锁,防止动画叠加

// 边界处理函数:确保索引不超出页面范围
function shift(newIndex) {
  if (newIndex < 0) return 0
  if (newIndex >= offsets.length) return offsets.length - 1
  return newIndex
}

window.addEventListener("wheel", (e) => {
  if (isAnimating) return // 动画未完成时不响应滚轮事件
  e.preventDefault() // 阻止浏览器默认滚动行为

  let targetIndex = currentIndex
  if (e.deltaY > 0) {
    targetIndex = shift(currentIndex + 1)
  } else if (e.deltaY < 0) {
    targetIndex = shift(currentIndex - 1)
  }
  if (targetIndex === currentIndex) return // 已经在目标页面,无需执行动画

  const targetOffset = offsets[targetIndex]
  const offsetDiff = targetOffset - currentOffset // 计算当前位置到目标位置的差值

  createAnimationFrames((delta) => {
    // 基于当前偏移量 + 差值*动画进度,计算实时位置
    const currentAnimOffset = currentOffset + offsetDiff * delta
    scrollerContent.style.transform = `translate3d(0, ${currentAnimOffset}vh, 0)`
  }, 1000, () => {
    // 动画完成后更新状态,解锁动画
    currentIndex = targetIndex
    currentOffset = targetOffset
    isAnimating = false
  })

  isAnimating = true
})

// 增强版requestAnimationFrame工具函数,增加完成回调
function createAnimationFrames(callback, duration, onComplete) {
  const start = performance.now() // 用performance.now()获取更高精度的时间戳
  const raf = requestAnimationFrame

  const animate = () => {
    const now = performance.now()
    const delta = Math.min((now - start) / duration, 1)

    callback(delta)

    if (delta < 1) {
      raf(animate)
    } else {
      onComplete?.()
    }
  }

  raf(animate)
}

关键修改说明

  1. currentOffset变量:实时记录滑块的当前偏移值,动画不再从0开始,而是基于这个值计算过渡路径
  2. offsetDiff差值计算:动画过程中是从当前位置向目标位置移动,而非直接跳到目标页面的绝对偏移
  3. isAnimating动画锁:防止滚轮事件在动画未完成时触发新的动画,避免位置冲突
  4. performance.now():替代new Date()获取时间戳,精度更高,动画更流畅
  5. 动画完成回调:确保动画结束后才更新索引和当前偏移,保证状态一致性

内容的提问来源于stack exchange,提问作者Amia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 01:10:18