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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 19:03:30