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

如何在React中控制滚轮滚动距离,实现每次固定滚动100像素

解决方案

问题1:preventDefault 不生效修复

React 合成事件体系中,wheel 事件默认被标记为 passive: true,浏览器会直接忽略回调内的 preventDefault() 调用,原生滚动行为还是会执行。你需要改用原生事件绑定,主动指定 passive: false 才能成功阻止默认滚动。

问题2:自定义平滑滚动时长

原生 scrollBy 的 smooth 动画参数由浏览器内核决定,不支持自定义时长。你可以抛弃内置的平滑配置,用 requestAnimationFrame 自己实现可控的平滑滚动逻辑,同时加入动画锁避免快速滚动时的动画堆积。

完整实现代码

import { useRef, useEffect } from 'react';

const ScrollList = () => {
  const listRef = useRef(null);
  // 滚动动画状态锁
  const isScrollingRef = useRef(false);
  // 累计待滚动距离
  const pendingScrollRef = useRef(0);

  // 自定义平滑滚动函数,支持自定义时长
  const smoothScroll = (targetDelta, duration = 150) => {
    const element = listRef.current;
    if (!element) return;

    const startScrollTop = element.scrollTop;
    const startTime = performance.now();

    const animateScroll = (currentTime) => {
      const elapsed = currentTime - startTime;
      const progress = Math.min(elapsed / duration, 1);
      // 缓动函数,可根据需求替换为线性/其他缓动效果
      const easeProgress = progress < 0.5 
        ? 4 * progress * progress * progress 
        : 1 - Math.pow(-2 * progress + 2, 3) / 2;
      
      element.scrollTop = startScrollTop + targetDelta * easeProgress;

      if (progress < 1) {
        requestAnimationFrame(animateScroll);
      } else {
        isScrollingRef.current = false;
        // 处理动画过程中新增的待滚动距离
        if (pendingScrollRef.current !== 0) {
          const nextDelta = pendingScrollRef.current;
          pendingScrollRef.current = 0;
          smoothScroll(nextDelta, duration);
        }
      }
    };

    isScrollingRef.current = true;
    requestAnimationFrame(animateScroll);
  };

  useEffect(() => {
    const listElement = listRef.current;
    if (!listElement) return;

    const handleWheel = (e) => {
      e.preventDefault();
      const delta = Math.sign(e.deltaY) * 100;
      
      if (!isScrollingRef.current) {
        // 第二个参数为动画时长,单位ms,可自行调整
        smoothScroll(delta, 150);
      } else {
        // 动画进行中就把滚动距离叠加到待执行队列
        pendingScrollRef.current += delta;
      }
    };

    // 绑定原生wheel事件,指定passive为false
    listElement.addEventListener('wheel', handleWheel, { passive: false });

    return () => {
      listElement.removeEventListener('wheel', handleWheel);
    };
  }, []);

  return (
    <div 
      ref={listRef}
      style={{ width: 300, height: 500, overflow: 'auto', border: '1px solid #eee' }}
    >
      {/* 替换为你的列表内容 */}
      {Array.from({ length: 50 }).map((_, i) => (
        <div key={i} style={{ height: 100, padding: '0 20px', lineHeight: '100px', borderBottom: '1px solid #f5f5f5' }}>
          列表项 {i + 1}
        </div>
      ))}
    </div>
  );
};

export default ScrollList;

注意事项

  • 调整 smoothScroll 调用时的第二个参数即可修改动画时长,数值越小滚动速度越快
  • 代码中的缓动函数可根据需求替换,想要线性滚动直接用 progress 代替 easeProgress 即可
  • 待滚动队列的逻辑可以保证快速滚动时不会丢步,所有滚动操作都会依次执行

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 12:36:05