水平滚动组件滚动至边界后如何触发垂直滚动
实现方案
核心逻辑为滚轮事件触发时先判断水平滚动容器是否已到达对应方向的边界,未到边界时执行自定义水平滚动,到达边界后不阻止事件冒泡,自然触发父容器的垂直滚动,该逻辑天然兼容鼠标滚轮与触控板操作,无需额外做设备适配。
完整实现代码
import { useRef } from 'react'; const HorizontalScrollList = () => { // 水平滚动容器ref const scrollContainerRef = useRef(null); const handleWheel = (e) => { const container = scrollContainerRef.current; if (!container) return; // 优先保留触控板原生左右水平滚动手势,不做拦截 if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) return; const { scrollLeft, scrollWidth, clientWidth } = container; // 判断滚动方向:deltaY大于0为滚轮向下/触控板向下滑,对应水平向右滚动 const isScrollToRight = e.deltaY > 0; // 边界判断:已到右边界且继续向右滚动,或已到左边界且继续向左滚动,不拦截事件,冒泡触发垂直滚动 // 加1px容错是避免部分浏览器滚动精度问题导致边界判断失效 if ( (isScrollToRight && scrollLeft + clientWidth >= scrollWidth - 1) || (!isScrollToRight && scrollLeft <= 1) ) { return; } // 未到边界时拦截默认滚动事件,执行水平滚动 e.preventDefault(); // 1.5为滚动速度系数,可根据实际体验调整 container.scrollLeft += e.deltaY * 1.5; }; return ( <div ref={scrollContainerRef} onWheel={handleWheel} style={{ width: '100%', overflowX: 'auto', display: 'flex', gap: '16px', padding: '16px', /* 隐藏滚动条可以加下面两个属性,不需要可以删除 */ // scrollbarWidth: 'none', // msOverflowStyle: 'none' }} > {/* 替换为你的列表内容 */} {Array.from({ length: 20 }).map((_, index) => ( <div key={index} style={{ minWidth: '220px', height: '160px', backgroundColor: '#f0f0f0', borderRadius: '8px', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }} > 卡片 {index + 1} </div> ))} </div> ); }; export default HorizontalScrollList;
注意事项
- 如果需要平滑滚动效果,可以将修改
scrollLeft的逻辑替换为container.scrollBy({ left: e.deltaY * 1.5, behavior: 'smooth' }),注意平滑滚动在长列表下可能存在性能损耗,可根据实际场景选择。 - 若你的滚动容器本身就是页面根容器,不需要额外调整,逻辑会自然触发页面的垂直滚动。
- 边界判断的容错值可以根据实际测试情况调整,避免出现滚动到边界后卡顿一次才触发垂直滚动的问题。
内容的提问来源于stack exchange,提问作者U. Watt
相关产品推荐
相关产品推荐

