JavaScript需通过touch事件监听、时间戳计算瞬时速度、requestAnimationFrame减速动画及边界回弹实现惯性滚动,核心是touchend时以平均速度为初速度并指数衰减位移。

JavaScript 本身不直接提供“惯性滚动”效果,而是通过监听触摸事件(touchstart、touchmove、touchend),结合速度估算与定时动画(如 requestAnimationFrame)模拟出滑动后继续滚动的物理感。关键在于记录滑动结束时的瞬时速度,并用缓动函数(如 ease-out)逐步衰减位移。
捕获滑动速度的关键时间点
仅靠 touchmove 的位移差不够,必须结合时间戳计算瞬时速度:
- 在
touchstart记录初始位置和时间 - 在
touchmove中持续更新最新位置和时间,用于实时速度估算(例如每 100ms 计算一次 Δy / Δt) - 在
touchend瞬间,取最后几次移动的速度平均值作为“初速度”,这是惯性启动的核心依据
用 requestAnimationFrame 实现平滑减速动画
避免使用 setTimeout 或 setInterval,它们帧率不稳定,易卡顿:
- 在
touchend触发后,启动一个 rAF 循环,每次更新滚动位置:当前位移 += 当前速度 × 时间步长 - 同时按指数或二次方衰减速度(例如
velocity *= 0.96),模拟阻力 - 当速度低于阈值(如 0.5px/frame)或位移停止变化时,终止动画
边界处理与物理合理性
真实体验离不开对滚动边界的响应:
- 到达容器顶部/底部时,不要硬性截断,可加入轻微“回弹”(overscroll):让位移短暂超出边界,再用弹性公式拉回
- 若启用
overflow: scroll原生滚动,需先preventDefault()所有触摸事件,否则系统默认行为会干扰自定义逻辑 - 注意 iOS Safari 的 “scroll blocking” 行为:未阻止默认事件时,
touchmove可能被节流,影响速度采样精度
简化实现参考(核心逻辑)
以下不是完整组件,而是体现惯性滚动主干的伪代码逻辑:
let startY = 0, startTime = 0, lastY = 0, lastTime = 0;
let velocityY = 0, isScrolling = false;
<p>el.addEventListener('touchstart', e => {
startY = e.touches[0].pageY;
startTime = lastTime = performance.now();
lastY = startY;
});</p><p>el.addEventListener('touchmove', e => {
const y = e.touches[0].pageY;
const now = performance.now();
if (now - lastTime > 16) { // 约每帧更新一次速度
velocityY = (y - lastY) / (now - lastTime) * 1000; // px/s
lastY = y;
lastTime = now;
}
});</p><p>el.addEventListener('touchend', () => {
if (Math.abs(velocityY) </p><p>isScrolling = true;
function inertiaLoop() {
if (!isScrolling) return;
const delta = velocityY <em> 16 / 1000; // 16ms 步长位移
el.scrollTop += delta;
velocityY </em>= 0.94; // 衰减系数</p><pre class="brush:php;toolbar:false;">if (Math.abs(velocityY) > 0.5) {
requestAnimationFrame(inertiaLoop);
} else {
isScrolling = false;
}} requestAnimationFrame(inertiaLoop); });
不复杂但容易忽略:实际项目中建议用成熟库(如 hammer.js、fullpage.js 或 CSS scroll-behavior: smooth + overscroll-behavior)来覆盖多数场景;手写主要用于定制化强、需精细控制性能的交互层。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











