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

R3F场景中Locomotive-scroll模态框滚动失效问题求助

解决React Three Fiber模态框中Locomotive Scroll滚动阻塞问题

核心问题分析

在R3F场景中,Three.js的canvas默认会捕获鼠标/触摸事件,可能拦截模态框的滚动事件;同时Locomotive Scroll容器样式配置不当、模态框动态显示时未正确初始化滚动实例,都会导致平滑滚动失效,只能使用原生滚动条。

修复步骤

1. 配置正确的容器样式

Locomotive Scroll需要容器具备明确的尺寸和溢出设置,确保滚动区域可被识别:

.modal_infos {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  background: rgba(0,0,0,0.8);
  z-index: 9999; /* 确保模态框层级高于R3F canvas */
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-scroll-container {
  width: 80%;
  height: 80vh;
  overflow: hidden; /* 隐藏原生滚动条 */
  position: relative;
}

.text_modale_div {
  padding: 2rem;
}

2. 阻止R3F canvas捕获模态框区域的滚动事件

在R3F的Canvas组件上添加事件过滤,模态框显示时禁止canvas处理滚动/触摸事件:

import { Canvas } from '@react-three/fiber';
import { useState } from 'react';

function Scene() {
  const [modalVisible, setModalVisible] = useState(false);

  const handlePointerEvent = (e) => {
    if (modalVisible) e.stopPropagation();
  };

  return (
    <>
      <Canvas
        onWheel={handlePointerEvent}
        onTouchStart={handlePointerEvent}
        onTouchMove={handlePointerEvent}
      >
        {/* 你的3D内容 */}
      </Canvas>
      <Modal visible={modalVisible} onClose={() => setModalVisible(false)} />
    </>
  );
}

3. 修正Locomotive Scroll初始化逻辑

模态框为动态显示组件,需在可见时初始化滚动实例,隐藏时销毁,避免内存泄漏:

import React, { useEffect, useRef } from 'react';
import locomotiveScroll from "locomotive-scroll";

export default function Modal({ visible, onClose }) {
    const scrollRef = useRef(null);
    const scrollInstance = useRef(null);

    useEffect(() => {
        if (visible && scrollRef.current) {
            scrollInstance.current = new locomotiveScroll({
                el: scrollRef.current,
                smooth: true,
                direction: 'vertical',
                gestureDirection: 'vertical'
            });

            return () => scrollInstance.current?.destroy();
        } else if (!visible && scrollInstance.current) {
            scrollInstance.current.destroy();
            scrollInstance.current = null;
        }
    }, [visible]);

    if (!visible) return null;

    return (
        <div className="modal_infos">
            <div className="modal-scroll-container" ref={scrollRef} data-scroll-container>
                <div className="close_icon" title="Fermer la fenêtre" onClick={onClose}>
                    <svg xmlns="http://www.w3.org/2000/svg" width="46" height="46" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round" className="icon icon-tabler icons-tabler-outline icon-tabler-x">
                        <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
                        <path d="M18 6l-12 12" />
                        <path d="M6 6l12 12" />
                    </svg>
                </div>

                <div className="text_modale_div" data-scroll-section>
                    <div className="text_page">
                        <h1 id="title_page" data-scroll data-scroll-speed="3" data-scroll-position="top">
                            Some txt to try
                        </h1>
                        <p id="first_text__page">
                            Lorem ipsum, dolor sit amet consectetur adipisicing elit. Atque eum est dolor repellat deleniti commodi debitis blanditiis cumque sed molestias facilis nesciunt cupiditate, quasi porro? Rerum, maiores. Sapiente, quia ducimus!
                        </p>
                        <div id="second_div_text_page">
                            Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat quod culpa sit, ad sunt doloribus assumenda voluptates accusamus labore temporibus distinctio. Esse eius est, maiores at aspernatur impedit molestias numquam?
                        </div>

                        <p>
                            Lorem ipsum dolor, sit amet consectetur adipisicing elit. Sed earum aut voluptates natus repellat sit possimus maxime? Earum perspiciatis sit alias, eveniet, recusandae animi esse hic blanditiis fugit unde quos!
                        </p>
                        <p>
                            Lorem, ipsum dolor sit amet consectetur adipisicing elit. Recusandae debitis dolores eligendi neque ex, dolorum sint? Recusandae provident suscipit architecto ad dolorem aliquid, eligendi quisquam culpa corrupti nostrum corporis accusantium.
                        </p>
                    </div>
                </div>
                <div className="text_modale_div" id="metro_div" data-scroll-section>
                    <h1>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Recusandae debitis dolores eligendi neque ex, dolorum sint? Recusandae provident suscipit architecto ad dolorem aliquid, eligendi quisquam culpa corrupti nostrum corporis accusantium.</h1>
                </div>
            </div>
        </div>
    );
}

4. 用React状态替代jQuery操作

移除jQuery的DOM操作,改用React的props和状态控制模态框显示/隐藏,确保与Locomotive Scroll的生命周期同步。

关键注意事项

  • 模态框的z-index必须高于R3F canvas,防止事件被canvas优先捕获
  • Locomotive Scroll容器必须设置overflow: hidden,避免原生滚动条干扰平滑滚动
  • 动态组件需在可见时初始化滚动实例,隐藏时销毁,避免内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 15:44:54