如何实现CSS Smooth Scroll Stop停止滚动时缓动减速不骤停的效果
自定义惯性缓停滚动实现方案
核心实现逻辑
这个效果本质是接管浏览器原生滚动逻辑,模拟物理世界的摩擦减速效果,核心流程如下:
- 禁用默认滚动行为,阻断原生滚动的即时停止逻辑
- 全场景监听滚动输入(鼠标滚轮、触摸板、触屏滑动),实时计算当前滚动的瞬时速度
- 检测到用户停止滚动输入后,启动
requestAnimationFrame逐帧动画,按固定阻尼系数衰减滚动速度 - 当速度衰减到预设阈值以下时终止动画,完成缓停过程
可直接复用的实现代码
class SmoothScroll { constructor(options = {}) { // 可配置参数 this.damping = options.damping || 0.92; // 阻尼系数,越小减速越快,建议取值0.85-0.95 this.threshold = options.threshold || 0.5; // 速度低于该值时停止动画 this.syncScrollRate = options.syncScrollRate || 1; // 滚动速度倍率 // 内部状态 this.currentY = window.scrollY; this.targetY = this.currentY; this.velocity = 0; this.isAnimating = false; this.init(); } init() { // 重置原生滚动配置 document.documentElement.style.scrollBehavior = 'auto'; document.body.style.overscrollBehavior = 'none'; // 监听鼠标滚轮/触摸板输入 window.addEventListener('wheel', (e) => { e.preventDefault(); // 累加滚动速度 this.velocity += e.deltaY * this.syncScrollRate; // 未启动动画时拉起缓动流程 if (!this.isAnimating) { this.isAnimating = true; requestAnimationFrame(this.animate.bind(this)); } }, { passive: false }); // 监听触屏滑动(移动端适配) this.lastTouchY = 0; window.addEventListener('touchstart', (e) => { this.lastTouchY = e.touches[0].clientY; this.velocity = 0; }, { passive: true }); window.addEventListener('touchmove', (e) => { const currentTouchY = e.touches[0].clientY; const delta = this.lastTouchY - currentTouchY; this.velocity = delta * 8; // 触屏滑动速度倍率 this.lastTouchY = currentTouchY; if (!this.isAnimating) { this.isAnimating = true; requestAnimationFrame(this.animate.bind(this)); } }, { passive: true }); } animate() { // 按阻尼系数衰减速度 this.velocity *= this.damping; // 累加目标滚动位置 this.targetY += this.velocity; // 边界限制,避免滚出页面范围 const maxScroll = document.body.scrollHeight - window.innerHeight; this.targetY = Math.max(0, Math.min(this.targetY, maxScroll)); // 插值平滑滚动到目标位置 this.currentY += (this.targetY - this.currentY) * 0.8; window.scrollTo(0, this.currentY); // 速度低于阈值时终止动画 if (Math.abs(this.velocity) < this.threshold) { this.velocity = 0; this.isAnimating = false; return; } requestAnimationFrame(this.animate.bind(this)); } } // 页面加载完成后初始化 window.addEventListener('DOMContentLoaded', () => { new SmoothScroll({ damping: 0.9, syncScrollRate: 0.9 }); });
参数调优说明
- 阻尼系数damping:数值越大,减速过程越长、滑动跟手感越强,数值超过0.95会出现滑动飘、停不下来的问题,低于0.85会接近原生硬停的效果
- 速度阈值threshold:不建议调至0.1以下,会增加不必要的性能消耗,用户也感知不到极慢的位移
- 滚动倍率syncScrollRate:适配不同设备的滚轮灵敏度,觉得滚动太慢就调大,太灵敏就调小
兼容注意事项
- 如果页面有锚点跳转、JS主动控制滚动的逻辑,需要在类中补充对应的滚动方法,直接修改
targetY值即可,不要直接调用window.scrollTo,否则会打断缓动效果 - 代码直接调用原生滚动API实现位移,不会出现transform滚动导致的固定定位元素失效问题
- 兼容所有现代浏览器,无需额外polyfill
内容的提问来源于stack exchange,提问作者so_hell
相关产品推荐
相关产品推荐

