使用react-locomotive-scroll时@react-three/fiber无法渲染的问题
问题描述
- 集成
react-locomotive-scroll后,@react-three/fiber组件无法正常渲染;移除locomotive-scroll后组件显示恢复正常 - 附带滚动条重复显示的问题
- 该冲突在多个
@react-three/fiber示例中均出现
相关代码
_app.tsx
import { useRouter } from 'next/router'; import type { AppProps } from 'next/app'; import { useRef } from 'react'; import { LocomotiveScrollProvider as RLSProvider } from 'react-locomotive-scroll'; import 'locomotive-scroll/dist/locomotive-scroll.css'; import '@styles/globals.scss'; function MyApp({ Component, pageProps }: AppProps) { const { asPath } = useRouter(); const containerRef = useRef(null); return ( <RLSProvider options={{ smooth: true, // ... all available Locomotive Scroll instance options }} watch={ [ //..all the dependencies you want to watch to update the scroll. // Basicaly, you would want to watch page/location changes // For exemple, on Next.js you would want to watch properties like `router.asPath` (you may want to add more criterias if the instance should be update on locations with query parameters) ] } location={asPath} onLocationChange={(scroll: any) => scroll.scrollTo(0, { duration: 0, disableLerp: true })} containerRef={containerRef} > <div data-scroll-container ref={containerRef}> <Component {...pageProps} /> </div> </RLSProvider> ); } export default MyApp;
@react-three/fiber组件代码
import * as THREE from "three" import { useRef, useState} from "react" import { Canvas, extend, useFrame } from "@react-three/fiber" import { useTexture, shaderMaterial } from "@react-three/drei" interface ImageProps { scaleY: number; scaleX: number; cssY?: number | string; cssX?: number | string; image1: string; image2: string; right?: number | string; left?: number | string; } export const ImageFadeMaterial = shaderMaterial( { effectFactor: 1.2, dispFactor: 0, tex: undefined, tex2: undefined, disp: undefined }, ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }`, ` varying vec2 vUv; uniform sampler2D tex; uniform sampler2D tex2; uniform sampler2D disp; uniform float _rot; uniform float dispFactor; uniform float effectFactor; void main() { vec2 uv = vUv; vec4 disp = texture2D(disp, uv); vec2 distortedPosition = vec2(uv.x + dispFactor * (disp.r*effectFactor), uv.y); vec2 distortedPosition2 = vec2(uv.x - (1.0 - dispFactor) * (disp.r*effectFactor), uv.y); vec4 _texture = texture2D(tex, distortedPosition); vec4 _texture2 = texture2D(tex2, distortedPosition2); vec4 finalTexture = mix(_texture, _texture2, dispFactor); gl_FragColor = finalTexture; #include <tonemapping_fragment> #include <encodings_fragment> }` ) extend({ ImageFadeMaterial }) function FadingImage({ scaleY, scaleX, image1, image2 } : ImageProps) { const ref = useRef(null) const [texture1, texture2, dispTexture] = useTexture([image1, image2, "/displacement.jpg"]) const [hovered, setHover] = useState(false) useFrame(() => { ref.current.dispFactor = THREE.MathUtils.lerp(ref.current.dispFactor, hovered ? 1 : 0, 0.075) }) return ( <mesh onPointerOver={() => setHover(true)} onPointerOut={() => setHover(false)} scale={[scaleX, scaleY, 1]}> <planeGeometry /> <imageFadeMaterial ref={ref} tex={texture1} tex2={texture2} disp={dispTexture} toneMapped={false} /> </mesh> ) } const ImageShader = ({ scaleY, scaleX, cssY, cssX, image1, image2, right, left } : ImageProps) => { return ( <div style={{ height: cssY, width: cssX, marginRight: right, marginLeft: left, }}> <Canvas camera={{ position: [0, 0, 2], fov: 50 }}> <FadingImage scaleX={scaleX} scaleY={scaleY} image1={image1} image2={image2}/> </Canvas> </div> ) } export default ImageShader
解决方案
1. 修复渲染冲突
Locomotive Scroll会修改容器的样式和事件监听,干扰Three.js画布渲染。可以通过阻止事件冒泡、配置Canvas独立上下文解决:
修改ImageShader组件:
import { useEffect, useRef } from "react" const ImageShader = ({ scaleY, scaleX, cssY, cssX, image1, image2, right, left } : ImageProps) => { const canvasContainerRef = useRef<HTMLDivElement>(null); useEffect(() => { if (canvasContainerRef.current) { // 阻止Locomotive Scroll捕获画布区域的滚动/触摸事件 canvasContainerRef.current.addEventListener('wheel', (e) => e.stopPropagation()); canvasContainerRef.current.addEventListener('touchmove', (e) => e.stopPropagation()); } }, []); return ( <div ref={canvasContainerRef} style={{ height: cssY, width: cssX, marginRight: right, marginLeft: left, }} > <Canvas camera={{ position: [0, 0, 2], fov: 50 }} gl={{ preserveDrawingBuffer: true }} > <FadingImage scaleX={scaleX} scaleY={scaleY} image1={image1} image2={image2}/> </Canvas> </div> ) }
2. 解决滚动条重复问题
Locomotive Scroll默认生成自定义滚动条,若和原生滚动条重复,可配置禁用原生滚动条:
修改_app.tsx的Locomotive配置:
<RLSProvider options={{ smooth: true, scrollbar: true, nativeScroll: false, // 禁用原生滚动条 }} watch={[asPath]} location={asPath} onLocationChange={(scroll: any) => scroll.scrollTo(0, { duration: 0, disableLerp: true })} containerRef={containerRef} >
或添加全局CSS:
body { overflow: hidden; }
3. 极端情况处理
若上述方法无效,可将Three.js组件移出Locomotive的滚动容器:
修改_app.tsx结构:
function MyApp({ Component, pageProps }: AppProps) { const { asPath } = useRouter(); const containerRef = useRef(null); return ( <div> <div id="threejs-container"></div> <RLSProvider options={{ smooth: true }} watch={[asPath]} location={asPath} onLocationChange={(scroll: any) => scroll.scrollTo(0, { duration: 0, disableLerp: true })} containerRef={containerRef} > <div data-scroll-container ref={containerRef}> <Component {...pageProps} /> </div> </RLSProvider> </div> ); }
修改ImageShader的Canvas容器:
<Canvas camera={{ position: [0, 0, 2], fov: 50 }} container={document.getElementById('threejs-container')} >
内容的提问来源于stack exchange,提问作者MagicalPineapple
相关产品推荐
相关产品推荐

