如何在react-three-fiber的Canvas中插入JSX组件并实现同等滚动效果
实现方案
你要的效果完全可以实现,核心要用到@react-three/drei提供的HTML组件,它可以把普通JSX/DOM元素渲染到Three.js 3D空间的指定坐标位,天然适配ScrollControls的滚动逻辑,定位方式和原示例的Image组件完全一致即可。
步骤1:引入HTML组件
在导入部分新增HTML的引入:
import { ScrollControls, Scroll, Preload, Image as ImageImpl, HTML } from '@react-three/drei'
步骤2:改造Card组件适配3D坐标体系
把原来的普通DOM用HTML组件包裹,匹配3D空间的定位、缩放逻辑:
function Card(props) { return ( // HTML组件继承所有Three.js Object3D的属性,position、scale都可以直接用 <group {...props}> <HTML transform occlude> <div style={{ background: 'red', width: 200, height: 500, borderRadius: 8, padding: 16, boxSizing: 'border-box' }}> <h3>hello world</h3> <p>great text here!</p> </div> </HTML> </group> ) }
参数说明:
transform:开启后DOM会跟随3D空间的缩放、旋转、位移变化,和3D元素对齐逻辑一致occlude:可选,开启后DOM会被其他3D物体遮挡,符合真实3D空间的层级逻辑
步骤3:按原Page的定位规则放置Card
你只需要把Card当成普通的Page组件,用和其他Page完全相同的定位规则赋值position属性即可,滚动效果会和原示例完全一致:
function Pages() { const { width } = useThree((state) => state.viewport) const w = width < 10 ? 1.5 / 3 : 1 / 3 return ( <> <Page position={[-width * 1, 0, 0]} urls={['/trip1.jpg', '/trip2.jpg', '/trip3.jpg']} /> <Page position={[width * 0, 0, 0]} urls={['/img1.jpg', '/img2.jpg', '/img3.jpg']} /> <Page position={[width * 1, 0, 0]} urls={['/img4.jpg', '/img5.jpg', '/img6.jpg']} /> {/* 插入Card,定位和前后Page保持相同的步长即可 */} <Card position={[width * 2, 0, 0]} scale={[width * w - 0.4 * 2, 5, 1]} /> <Page position={[width * 3, 0, 0]} urls={['/trip1.jpg', '/trip2.jpg', '/trip3.jpg']} /> <Page position={[width * 4, 0, 0]} urls={['/img1.jpg', '/img2.jpg', '/img3.jpg']} /> <Page position={[width * 5, 0, 0]} urls={['/img4.jpg', '/img5.jpg', '/img6.jpg']} /> </> ) }
注意事项
- 放在
Scroll组件内的所有元素,位移都会被ScrollControls自动接管,只要你的定位步长和原Page保持一致,滚动流畅度、无限滚动效果和原示例没有任何区别 - 如果需要调整DOM的大小匹配周围的Image组件,要么调整
HTML的scale属性,要么直接修改内部div的像素尺寸即可 - 如果不需要3D遮挡效果,可以删掉
occlude属性,减少性能开销
内容的提问来源于stack exchange,提问作者HelloWorldEngineer
相关产品推荐
相关产品推荐

