如何在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
相关产品推荐
相关产品推荐

