如何在React中实现滚轮方向动态切换与恢复的特殊滚动交互?
React 滚动交互实现方案
核心思路
通过监听全局滚轮事件,在页面2进入视口的区间内拦截垂直滚动行为,将滚轮偏移量转换为水平滚动的偏移量,同时精准判断滚动边界,完成后恢复垂直滚动;向上滚动时反向执行逻辑。
具体实现步骤
1. 页面结构与Ref设置
用容器包裹垂直页面和水平滚动区间,给关键元素添加Ref以获取位置和尺寸:
import { useRef, useEffect, useState } from 'react'; const ScrollLayout = () => { const page2Ref = useRef(null); const horizontalWrapRef = useRef(null); const [inHorizontalZone, setInHorizontalZone] = useState(false); const [horizontalScrollDone, setHorizontalScrollDone] = useState(false); // 后续逻辑写在此处 return ( <div className="full-scroll-container"> <div className="page page-1">页面1</div> <div ref={page2Ref} className="page page-2">页面2</div> <div ref={horizontalWrapRef} className="horizontal-scroll-wrap"> <div className="page page-3">页面3</div> </div> <div className="page page-4">页面4</div> </div> ); };
2. 判断滚动区间状态
监听页面滚动,计算页面2的视口位置和水平容器的滚动完成状态:
useEffect(() => { const checkScrollZone = () => { if (!page2Ref.current || !horizontalWrapRef.current) return; const page2Rect = page2Ref.current.getBoundingClientRect(); const maxHorizontalScroll = horizontalWrapRef.current.scrollWidth - horizontalWrapRef.current.clientWidth; // 判断是否进入水平滚动触发区间(页面2进入视口) setInHorizontalZone(page2Rect.top <= window.innerHeight && page2Rect.bottom > 0); // 判断水平滚动是否完全到位(留10px误差避免边界判断过严) setHorizontalScrollDone(horizontalWrapRef.current.scrollLeft >= maxHorizontalScroll - 10); }; window.addEventListener('scroll', checkScrollZone); return () => window.removeEventListener('scroll', checkScrollZone); }, []);
3. 拦截滚轮事件控制滚动方向
监听滚轮事件,在目标区间内替换滚动行为,处理边界恢复逻辑:
useEffect(() => { const handleWheel = (e) => { // 不在水平区间或已完成水平滚动,直接放行垂直滚动 if (!inHorizontalZone || horizontalScrollDone) return; // 向上滚动且水平容器已回到起点,放行垂直滚动 if (e.deltaY < 0 && horizontalWrapRef.current.scrollLeft <= 0) { return; } // 拦截默认垂直滚动,转换为水平滚动 e.preventDefault(); horizontalWrapRef.current.scrollBy({ left: e.deltaY * 1.2, // 1.2为滚动速度系数,可按需调整 behavior: 'auto' // 保持原生滚动手感 }); }; // passive设为false才能调用preventDefault window.addEventListener('wheel', handleWheel, { passive: false }); return () => window.removeEventListener('wheel', handleWheel); }, [inHorizontalZone, horizontalScrollDone]);
4. 配套样式设置
给水平容器设置溢出和固定高度,确保水平滚动生效:
.full-scroll-container { width: 100%; } .page { height: 100vh; width: 100%; } .horizontal-scroll-wrap { height: 100vh; width: 100%; overflow-x: auto; white-space: nowrap; } .page-3 { display: inline-block; width: 100vw; }
解决useInView的适配问题
useInView仅能判断元素是否进入视口,无法处理连续滚动的速度适配和边界精准控制。上述方案通过直接监听滚轮事件,实时计算滚动位置与偏移量,能精准控制滚动切换时机,避免页面1/3部分渲染的问题。
内容的提问来源于stack exchange,提问作者zulian adryan
相关产品推荐
相关产品推荐

