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

onWheel事件优化:实现滚动时立即触发方法而非停止后执行

嘿,我完全懂你遇到的这个糟心问题——连续滚动滚轮时,scrollIntoView的平滑动画根本没机会启动,非得等滚轮停了才动,这体验太别扭了。本质原因是:浏览器在处理连续的wheel事件时,会不断触发你的事件处理函数,而原生的平滑滚动动画是异步的,新的事件会直接打断或覆盖掉之前的动画任务,导致它永远没机会执行。

给你两个靠谱的解决方案,按需选就行:


方案一:用「锁机制」保护原生平滑滚动

核心思路是加一个标志位,确保同一时间只有一个滚动动画在执行,避免连续事件打断它:

// 标记当前是否正在执行滚动动画
let isScrolling = false;

window.addEventListener("wheel", e => {
  // 如果正在滚动,直接忽略当前事件
  if (isScrolling) {
    e.preventDefault();
    return;
  }

  e.preventDefault();
  
  const delta = e.detail ? e.detail * (-120) : e.wheelDelta;
  const direction = delta > 0 ? 'up' : 'down';

  let targetElement;
  if (direction === "down") {
    targetElement = document.querySelector('.who');
  } else if (direction === "up") {
    targetElement = document.querySelector('.why');
  }

  if (targetElement) {
    isScrolling = true;
    // 执行原生平滑滚动
    targetElement.scrollIntoView({ alignToTop: true, behavior: 'smooth'});
    
    // 监听动画结束事件,重置锁
    const onScrollFinish = () => {
      isScrolling = false;
      targetElement.removeEventListener('transitionend', onScrollFinish);
      targetElement.removeEventListener('webkitTransitionEnd', onScrollFinish); // 兼容Safari等webkit内核浏览器
    };
    
    targetElement.addEventListener('transitionend', onScrollFinish);
    targetElement.addEventListener('webkitTransitionEnd', onScrollFinish);
  }
});

这个方案的好处是:

  • 保留了浏览器原生的平滑滚动效果,代码简洁
  • 通过锁机制避免了连续事件的干扰,滚动触发时机变得即时

方案二:手动控制滚动动画(兼容性更好)

如果你的项目需要兼容不支持scrollIntoView平滑特性的旧浏览器,或者想要更自定义的滚动效果,可以用requestAnimationFrame手动实现滚动:

let isScrolling = false;
const SCROLL_STEP = 60; // 每次滚动的步长,数值越大滚动越快

window.addEventListener("wheel", e => {
  if (isScrolling) {
    e.preventDefault();
    return;
  }

  e.preventDefault();
  const delta = e.detail ? e.detail * (-120) : e.wheelDelta;
  const direction = delta > 0 ? 'up' : 'down';
  
  let targetOffset;
  if (direction === "down") {
    targetOffset = document.querySelector('.who').offsetTop;
  } else if (direction === "up") {
    targetOffset = document.querySelector('.why').offsetTop;
  }

  if (targetOffset !== undefined) {
    isScrolling = true;
    const currentOffset = window.scrollY;
    const totalDistance = targetOffset - currentOffset;
    const totalSteps = Math.abs(totalDistance) / SCROLL_STEP;
    let currentStep = 0;

    const animateScroll = () => {
      currentStep++;
      // 计算当前要滚动到的位置
      const currentPosition = currentOffset + (totalDistance / totalSteps) * currentStep;
      window.scrollTo(0, currentPosition);

      if (currentStep < totalSteps) {
        requestAnimationFrame(animateScroll);
      } else {
        // 最后一步确保精准滚动到目标位置
        window.scrollTo(0, targetOffset);
        isScrolling = false;
      }
    };

    requestAnimationFrame(animateScroll);
  }
});

这个方案的优势是:

  • 不依赖浏览器原生的平滑滚动API,兼容性拉满
  • 可以自由调整滚动速度、缓动效果(比如加个缓动函数)
  • 完全不受连续wheel事件的干扰,触发即时性拉满

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:27:04