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

为何JavaScript实现Lerp位置过渡无缓动效果?求技术解答

问题原因

你当前的代码里,每次调用lerp(0, 300, t)都是基于初始位置计算,而t = elapsed/duration是匀速从0增长到1的,所以算出的pos必然是匀速变化的,和300*t完全等价,自然没有缓动效果。

解决方案

要实现缓动过渡,有两种常用的可行方式:

方式1:基于当前位置的渐进式Lerp

这种方式是每帧从当前位置向目标位置移动一小步,移动距离是当前与目标差值的固定比例,物体速度会随接近目标逐渐减慢,自然形成缓动效果。

修改后的完整代码:

const lerp = (start, end, factor) => start + (end - start) * factor

const div = document.querySelector('div')
const btn = document.querySelector('button')

let pos = 0
const targetPos = 300
// 每帧移动的比例,0~1之间,值越小缓动越明显
const moveFactor = 0.05
let aF = null

const animate = () => {
  // 从当前位置向目标位置插值
  pos = lerp(pos, targetPos, moveFactor)
  
  // 距离目标足够近时停止动画,避免浮点精度问题
  if (Math.abs(pos - targetPos) > 0.1) {
    aF = requestAnimationFrame(animate)
  } else {
    cancelAnimationFrame(aF)
    aF = null
    pos = targetPos
  }

  div.style.transform = `translateX(${pos}px)`
}

btn.addEventListener('click', () => {
  pos = 0
  aF = requestAnimationFrame(animate)
})
div {
  width: 50px;
  height: 50px;
  background: green;
}

button {
  margin-bottom: 10px; 
}
<button>Run Animation</button>
<div></div>
  • moveFactor控制缓动强度:值越小(比如0.02),减速越明显,到达目标的时间越长;值越大(比如0.2),越接近匀速。
  • 加入Math.abs(pos - targetPos) > 0.1的判断,防止因浮点精度问题导致动画无法停止。

方式2:用缓动函数处理时间因子t

如果需要严格控制动画时长,可以保持基于初始位置的插值,但对t值应用缓动函数,让t的增长不再线性,从而实现不同的缓动曲线(先快后慢、先慢后快等)。

比如实现先快后慢的二次缓出效果:

const lerp = (start, end, t) => start + (end - start) * t

// 二次缓出函数:t从0到1,输出从0到1,变化先快后慢
const easeOutQuad = t => 1 - Math.pow(1 - t, 2)

const div = document.querySelector('div')
const btn = document.querySelector('button')

let pos = 0
let startTime = 0
const duration = 2000
let aF = null

const animate = () => {
  const elapsed = Date.now() - startTime
  let t = elapsed / duration
  // 限制t不超过1,避免超出目标位置
  t = Math.min(t, 1)
  
  // 用缓动函数处理t值,得到非线性的进度
  const easedT = easeOutQuad(t)
  pos = lerp(0, 300, easedT)

  if (elapsed < duration) {
    aF = requestAnimationFrame(animate)
  } else {
    cancelAnimationFrame(aF)
    aF = null
    pos = 300
  }

  div.style.transform = `translateX(${pos}px)`
}

btn.addEventListener('click', () => {
  pos = 0
  startTime = Date.now()
  aF = requestAnimationFrame(animate)
})
  • 你可以替换缓动函数实现不同效果:
    • 缓入(先慢后快):const easeInQuad = t => t * t
    • 缓入缓出(先慢、中快、后慢):const easeInOutQuad = t => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2
  • 这种方式能精确控制动画总时长,适合需要固定时长的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 11:20:36