使用react-three/drei的OrbitControls时,GLB模型区域如何实现页面滚动?
解决OrbitControls抢占滚动捏合事件导致页面无法滚动的问题
针对你遇到的OrbitControls拦截滚动/捏合事件的问题,这里有几个实用的解决方案,覆盖不同的交互需求:
方案1:达到缩放边界时释放滚动事件
这个方案完全匹配你的需求——当模型缩放至最小/最大级别时,自动允许页面滚动,否则保持OrbitControls的缩放功能。
实现思路是监听滚轮事件,判断当前相机与模型目标点的距离是否触及minDistance或maxDistance,如果是,则取消OrbitControls对事件的拦截,让页面接收滚动信号。
import { useRef } from 'react'; import { OrbitControls } from '@react-three/drei'; export default function ModelViewer() { const controlsRef = useRef(null); const cameraRef = useRef(null); const handleWheel = (event) => { const controls = controlsRef.current; const camera = cameraRef.current; if (!controls || !camera) return; const currentDistance = camera.position.distanceTo(controls.target); const hitMinZoom = currentDistance <= controls.minDistance; const hitMaxZoom = currentDistance >= controls.maxDistance; // 向下滚动时已到最小缩放,或向上滚动时已到最大缩放,允许页面滚动 if ((hitMinZoom && event.deltaY > 0) || (hitMaxZoom && event.deltaY < 0)) { event.stopPropagation = () => {}; event.preventDefault = () => {}; } }; return ( <group> {/* 替换为你的GLB模型组件 */} {/* <GLBModel /> */} <OrbitControls ref={controlsRef} minDistance={5} // 根据你的模型尺寸调整 maxDistance={50} // 根据你的模型尺寸调整 enableZoom={true} onWheel={handleWheel} /> <perspectiveCamera ref={cameraRef} /> </group> ); }
方案2:仅在交互时启用缩放
如果不需要严格的边界控制,这个方案更简单——只有当用户点击/触摸模型区域时,才让OrbitControls接管缩放事件,平时滚动直接作用于页面。
import { useState } from 'react'; import { OrbitControls } from '@react-three/drei'; export default function ModelViewer() { const [isInteracting, setIsInteracting] = useState(false); return ( <group onPointerDown={() => setIsInteracting(true)} onPointerUp={() => setIsInteracting(false)} onPointerLeave={() => setIsInteracting(false)} > {/* 替换为你的GLB模型组件 */} {/* <GLBModel /> */} <OrbitControls enableZoom={isInteracting} enablePan={isInteracting} // 可选:同时禁用平移,避免误触 /> </group> ); }
方案3:重写OrbitControls的滚轮处理逻辑
如果需要更底层的控制,可以直接修改OrbitControls的内置滚轮事件处理函数,在达到边界时跳过缩放逻辑。
import { useRef, useEffect } from 'react'; import { OrbitControls } from '@react-three/drei'; export default function ModelViewer() { const controlsRef = useRef(null); useEffect(() => { const controls = controlsRef.current; if (!controls) return; // 保存原始的滚轮处理函数 const originalWheelHandler = controls.handleWheel; // 重写滚轮处理逻辑 controls.handleWheel = function (event) { const currentDistance = this.camera.position.distanceTo(this.target); const hitMinZoom = currentDistance <= this.minDistance; const hitMaxZoom = currentDistance >= this.maxDistance; // 触及边界时,不执行缩放,直接放行事件 if ((hitMinZoom && event.deltaY > 0) || (hitMaxZoom && event.deltaY < 0)) { return; } // 否则执行原始缩放逻辑 originalWheelHandler.call(this, event); }; // 组件卸载时恢复原始逻辑 return () => { if (controls) controls.handleWheel = originalWheelHandler; }; }, []); return ( <group> {/* 替换为你的GLB模型组件 */} {/* <GLBModel /> */} <OrbitControls ref={controlsRef} minDistance={5} maxDistance={50} enableZoom={true} /> </group> ); }
内容的提问来源于stack exchange,提问作者Poonam Kaushal
相关产品推荐
相关产品推荐

