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

如何实现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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 18:06:27