为何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
相关产品推荐
相关产品推荐

