尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

React 360 多 Surface 与 3D 混合应用实战:MultiRoot 示例源码级解析

发布时间:2026/9/25 5:49:40

资讯中心
01
ARTICLE

React 360 多 Surface 与 3D 混合应用实战:MultiRoot 示例源码级解析

React 360 多 Surface 与 3D 混合应用实战:MultiRoot 示例源码级解析
前端3D渲染【免费下载链接】react-360Create amazing 360 and VR content using React项目地址https://gitcode.com/gh_mirrors/re/react-360点击查看免费下载React 360 允许开发者在同一场景中挂载多个根节点Root既可以是左右并列的 2D 平面面板Flat Surface也可以是悬在空间中的 3D 模型挂载点Location。本文以仓库中官方示例 Samples/MultiRoot 为骨架完整讲解如何搭建一个左列表 右详情 中央 3D 模型的三合一 VR 应用并通过一个 Redux 风格的全局 Store 在多个 React 根之间同步数据。读完本文你将掌握renderToSurface与renderToLocation的配合用法、多根数据同步的通用模式以及如何把外部 APIGoogle Poly拉取的 GLTF2 模型渲染进 3D 空间。示例整体定位多 Surface 与 3D 的混合应用docs/example-multisurface.md对应的官方示例展示了一个典型的 React 360 应用形态在多块 2D 面板Surface与 3D 根Location之间共享代码与数据。它同时做到了两件事从 Google Poly API 拉取精选 3D 模型的元数据渲染成一个可点击的列表演示基础的 3D 渲染——把选中的模型下载下来在场景中央旋转展示。示例目录 Samples/MultiRoot 下的完整工程由 6 个文件构成文件职责index.js初始化 Store填入 API Key并注册三个 React 根组件client.js运行时React 360 Web 侧创建 Surface 与 Location挂载三个根Store.js全局数据 StoreRedux 风格跨根同步数据TopPosts.js左面板模型列表可点击CurrentPost.js右面板当前选中模型的标题、作者、描述ModelView.js3D 根渲染选中的 GLTF2 模型并旋转展示其中client.js负责空间布局它创建左右两个 Flat Surface以及一个用于放置 3D 模型的 Location然后把所有 React 节点挂载进场景。运行示例获取 API Key 与启动步骤要在本地跑起这个示例核心前置条件是Google Poly API 的 Key——Poly 是 Google 提供 3D 模型共享的服务示例依赖它获取模型数据。获取凭证的官方流程见 Google Poly 开发者文档需要注册 Google Cloud 项目并启用 Poly API。拿到 Key 后把它填进 index.js 中的Store.initialize()调用import {AppRegistry} from react-360; import TopPosts from ./TopPosts; import CurrentPost from ./CurrentPost; import ModelView from ./ModelView; import * as Store from ./Store; Store.initialize(API KEY HERE); AppRegistry.registerComponent(TopPosts, () TopPosts); AppRegistry.registerComponent(CurrentPost, () CurrentPost); AppRegistry.registerComponent(ModelView, () ModelView);启动方式分两种在示例目录内直接运行执行npm start启动开发服务器然后访问http://localhost:8081/示例 README 中的说明在自建新项目中复现先用 React 360 CLI 创建新项目再把本仓库Samples/MultiRoot下的文件复制进项目目录最后访问http://localhost:8081/index.html官方文档docs/example-multisurface.md中的步骤。启动后空间中会出现两块面板左侧面板快速加载来自 Google Poly 精选列表的数据每次运行结果会因数据源变化而不同点击左侧任意条目右侧面板会显示该模型的标题、作者与描述同时场景中央会下载并渲染该 3D 模型并持续旋转方便你从各个角度查看。注意来自官方文档由于数据来自外部 API应用拉取到的文件没有质量保证——模型可能过大或过小也可能格式不规范。该示例的目的仅在于演示如何在 2D 与 3D Surface 之间共享数据并未覆盖真实应用可能需要的全部校验逻辑。应用架构四个组成部分与数据流官方文档明确说明了代码的构成两个渲染左右面板的 React 组件、一个获取并展示 3D 模型的组件、一个同步所有组件的全局数据 Store。真实应用通常会选用成熟的状态库如 Redux但为了简化示例这里实现了一个极简的 Redux 风格 API。Store 中保存两类数据postsPoly 精选列表中的模型元数据数组current当前选中的条目索引初始为-1表示未选中。这套设计的核心思想是所有 React 根运行在同一个 JavaScript 环境中因此任何全局 Store 都可以被所有组件共享。点击左面板条目 → 更新 Store 的current字段 → 触发其余两个 React 根更新从而把当前选中的信息同步到右面板与 3D 视图中。空间布局client.js 中的三根挂载client.js 是运行时的入口它演示了 React 360 Web API 的两个关键方法——renderToSurface挂到 2D 面板与renderToLocation挂到 3D 空间import {ReactInstance, Location, Surface} from react-360-web; function init(bundle, parent, options {}) { const r360 new ReactInstance(bundle, parent, { fullScreen: true, ...options, }); // 创建三个根左右两块 Flat 面板以及一个用于放置 3D 模型的 Location const leftPanel new Surface(300, 600, Surface.SurfaceShape.Flat); leftPanel.setAngle(-0.6, 0); const rightPanel new Surface(300, 600, Surface.SurfaceShape.Flat); rightPanel.setAngle(0.6, 0); r360.renderToSurface( r360.createRoot(TopPosts), leftPanel, ); r360.renderToSurface( r360.createRoot(CurrentPost), rightPanel, ); r360.renderToLocation( r360.createRoot(ModelView), new Location([0, -2, -10]), ); r360.compositor.setBackground(./static_assets/360_world.jpg); } window.React360 {init};几点值得展开的细节Flat Surface 的尺寸与角度左右面板都是300 × 600像素的平面setAngle(-0.6, 0)把左面板向左偏转约 0.6 弧度约 34°右面板以setAngle(0.6, 0)对称偏右从而在用户面前形成双屏布局。其底层实现见 React360/js/Compositor/Surface.jssetAngle(yaw, pitch, roll)中yaw在水平面旋转面板、pitch在垂直方向移动、roll旋转面板自身并触发_recomputeOrientation()重新计算位置与四元数。Flat Surface 的几何原理从源码看Flat Surface 被定位在一个半径 4 米的虚拟球体外侧Surface.js面板中心始终正对用户其实际物理尺寸由像素尺寸、密度与半径共同决定createFlatGeometry。Location 与坐标系new Location([0, -2, -10])把 3D 模型挂载点放在用户前方 10 米、下方 2 米处。Location是 3D 空间中放置内容的锚点配合r360.renderToLocation(root, location)使用与 Surface 的 2D 渲染互补。背景全景r360.compositor.setBackground(./static_assets/360_world.jpg)设置 360 度全景背景静态资源位于 Samples/MultiRoot/static_assets/360_world.jpg。index.html中通过React360.init传入 bundle 与容器并指定bridgeFile: NonBlobBridge.js作为 Web Worker 桥接文件见 Samples/MultiRoot/index.html。全局 Store极简 Redux 风格实现Store.js 是整个示例的数据同步中枢实现了initialize、setCurrent、connect三个对外 API以及一个监听器集合listenersconst State { posts: undefined, current: -1, }; const listeners new Set(); function updateComponents() { for (const cb of listeners.values()) { cb(); } } const POLY_PATH https://poly.googleapis.com/v1/assets?; export function initialize(apiKey) { // 从 Google Poly 拉取 top 5 精选帖子 const options { curated: true, format: GLTF2, key: apiKey, pageSize: 5, }; const queryString Object.keys(options) .map(k ${k}${options[k]}) .join(); fetch(POLY_PATH queryString) .then(response response.json()) .then(body { const entries body.assets.map(asset { const objSource asset.formats.filter( format format.formatType GLTF2 )[0]; return { id: asset.name, name: asset.displayName, author: asset.authorName, description: asset.description, source: objSource, preview: asset.thumbnail.url, }; }); State.posts entries; updateComponents(); }); }各 API 的职责与实现要点initialize(apiKey)发起对 Poly API 的请求请求参数包括curated: true精选内容、format: GLTF2只关心 GLTF2 格式、pageSize: 5拉取 5 条。返回数据被规整为{id, name, author, description, source, preview}结构其中source保留该模型 GLTF2 格式的资源对象含root.url供后续下载模型用。setCurrent(value)更新State.current并广播更新。它是左面板点击事件的唯一入口也是整个跨根数据流的发令枪。connect(Component)高阶组件HOC。它把 Store 中的posts与current作为 props 注入到包裹的组件中并在componentDidMount时把监听回调注册进listeners在componentWillUnmount时移除。当 Store 广播更新时所有已连接的组件通过setState拿到最新数据——这正是多个 React 根得以同步的机制。从源码结构看这个 Store 虽然简单但已经具备了 Redux 最核心的三个要素单一状态源State、显式的状态更新函数setCurrent、以及订阅-通知机制listenersconnect。左面板 TopPosts可交互的模型列表TopPosts.js 由两段组成内部组件PostButton负责单个条目的渲染与交互外层TopPosts负责列表编排最后通过connect(TopPosts)导出。class PostButton extends React.Component { state { hover: false, }; render() { return ( VrButton style{styles.postButton} onEnter{() this.setState({hover: true})} onExit{() this.setState({hover: false})} onClick{() setCurrent(this.props.index)} Image style{styles.postButtonPreview} source{{uri: this.props.preview}} / View style{[styles.postButtonInfo, this.state.hover ? styles.postButtonInfoHover : null]} View style{styles.postButtonLabel} Text style{styles.postButtonName}{this.props.name}/Text /View View style{styles.postButtonLabel} Text style{styles.postButtonAuthor}{this.props.author}/Text /View /View /VrButton ); } }交互要点点击更新 StoreonClick{() setCurrent(this.props.index)}是数据流的起点——点击后setCurrent更新全局索引进而触发右面板与 3D 视图更新。悬停反馈通过onEnter/onExit维护hover状态配合样式切换postButtonInfoHover把半透明黑色背景变为全透明提供视觉反馈。远程缩略图Image source{{uri: this.props.preview}} /直接从 Poly 的缩略图 URL 加载图片缩略图高度为 225px。加载态外层组件在props.posts尚未就绪时渲染 Loading... 占位。样式层面styles对象值得注意面板wrapper固定为300 × 600与client.js中 Surface 的尺寸严格对应列表项高度 120px预览图height: 225通过overflow: hidden裁剪信息标签使用alignSelf: flex-start实现左对齐小色块效果。右面板 CurrentPost选中模型的详情展示CurrentPost.js 是一个纯展示组件同样通过connect(CurrentPost)注入数据。它处理了三种渲染状态posts未就绪返回空容器等待数据current 0显示居中的 Select a Model 提示初始状态已有选中项取props.posts[props.current]渲染模型名称fontSize: 30、作者fontSize: 20与描述fontSize: 16。这个组件体现了多根同步的直接效果它自己不发起任何请求或状态变更只依赖 Store 的current与posts因此左面板的每次点击都会自动反映到右面板上。3D 视图 ModelViewGLTF2 模型的加载与旋转ModelView.js 是整个示例中 3D 能力的集中体现它同时用到了Entity模型实体、AmbientLight/PointLight光照与Animated动画import * as React from react; import {Animated, View} from react-360; import Entity from Entity; import AmbientLight from AmbientLight; import PointLight from PointLight; import {connect} from ./Store; const AnimatedEntity Animated.createAnimatedComponent(Entity); class ModelView extends React.Component { rotation new Animated.Value(0); componentWillReceiveProps(nextProps) { if (nextProps.current ! this.props.current) { this.rotation.setValue(0); Animated.timing(this.rotation, {toValue: 360, duration: 20000}).start(); } } render() { if (!this.props.posts || this.props.current 0) { return null; } const post this.props.posts[this.props.current]; const source post.source; return ( View AmbientLight intensity{1.0} color{#ffffff} / PointLight intensity{0.4} style{{transform: [{translate: [0, 4, -1]}]}} / AnimatedEntity style{{transform: [{rotateY: this.rotation}]}} source{{gltf2: source.root.url}} / /View ); } } const ConnectedModelView connect(ModelView); export default ConnectedModelView;实现细节解析动态模型加载source{{gltf2: source.root.url}}直接把 Poly 返回的 GLTF2 资源 URL 传给Entity。Entity是 React 360 的 3D 模型原生组件支持gltf2与obj/mtl两种资源类型见 Libraries/Mesh/Entity.js 中定义的EntitySource类型。360° 旋转动画通过Animated.createAnimatedComponent(Entity)让模型支持动画值Animated.timing在 20 秒内把rotateY从 0 驱动到 360实现全方位展示切换模型时先setValue(0)重置再重新开始。光照组合AmbientLight intensity{1.0}提供整体环境光白色PointLight intensity{0.4}以translate: [0, 4, -1]定位在模型上方形成明暗层次。对应原生视图实现见 React360/js/Views/AmbientLight.js 与 React360/js/Views/PointLight.js。空态处理未选中模型时返回null3D 挂载点保持空白。多根数据同步一张图看懂完整数据流综合四个文件整个应用的数据流可以概括为一条闭环index.js启动时调用Store.initialize(apiKey)异步拉取 Poly 精选列表并写入State.posts广播后三个已connect的根组件同时拿到列表数据左面板渲染列表右面板等待选择用户点击PostButton→setCurrent(index)→ 更新State.current并广播CurrentPost与ModelView感知到current变化分别更新详情文本与 3D 模型ModelView通过componentWillReceiveProps检测变化重置并启动旋转动画。这一模式正是官方文档 docs/surfaces.md 中使用多 Surface 时如何共享数据一节的落地范例所有根组件共享同一个 JavaScript 环境任何全局 Store 都能在组件间同步。文档中还补充了两种更灵活的多根挂载方式详见 docs/surfaces.md// 不同组件挂到不同 Surface r360.renderToSurface( r360.createRoot(ComponentOne), firstSurface, ); r360.renderToSurface( r360.createRoot(ComponentTwo), secondSurface, ); // 同一组件挂到不同 Surface可传入不同初始 props r360.renderToSurface( r360.createRoot(SharedComponent, { /* initial props for first panel */ }), firstSurface, ); r360.renderToSurface( r360.createRoot(SharedComponent, { /* initial props for second panel */ }), secondSurface, );从示例到实战可以沿用的三条经验多根 ≠ 多环境所有 React 根共享同一 JS 环境因此全局 StoreRedux 或自研是跨根同步的标准解法示例的connectHOC 模式可以轻松替换为真正的 Reduxconnect或 React Context。Surface 尺寸与组件样式严格对应client.js中new Surface(300, 600, ...)与组件内wrapper: {width: 300, height: 600}必须一致否则内容会错位或溢出。外部数据要留校验余地官方文档特别提醒Poly 这类外部 API 返回的模型可能缩放异常或格式不规范真实应用应在展示前增加尺寸归一化与格式校验同时注意 GLTF2 资源的跨域CORS可访问性。如果需要进一步研究相关实现推荐按以下路径深入仓库Surface 的完整 APIsetAngle/setDensity/setRadius/setShape/recenter见 React360/js/Compositor/Surface.js多 Surface 使用指南见 docs/surfaces.mdEntity组件的资源类型定义见 Libraries/Mesh/Entity.js运行时挂载方法renderToSurface/renderToLocation/createRoot的调用方式见 React360/js/ReactInstance.js。赞分享前端3D渲染【免费下载链接】react-360Create amazing 360 and VR content using React项目地址https://gitcode.com/gh_mirrors/re/react-360点击查看免费下载相关推荐在 React 应用中集成 BlockSuitereact-basic 示例源码级实战指南在 React 应用中集成 BlockSuitereact basic 示例源码级实战指南 BlockSuite 的官方仓库在 examples/react前端UI组件富文本多根Multi-Root架构实战用 React 360 构建多面板与 3D 模型协同的 VR 应用多根Multi Root架构实战用 React 360 构建多面板与 3D 模型协同的 VR 应用 导读 本文基于仓库中的 Samples/MultiRo前端3D渲染React Native Elements Tile 组件实战从 Snack 示例到源码级解析React Native Elements Tile 组件实战从 Snack 示例到源码级解析 Tile磁贴是 React Native ElementsUI组件移动开发前端上一篇突破交通流预测瓶颈用SAEs/LSTM/GRU神经网络实现分钟级精准预测下一篇MaterialColorsApp与主流设计工具集成Figma、Sketch、Adobe XD工作流创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。