1

我正在使用React VR制作应用程序。如果你不了解 React VR,那么它基于 React Native 和一些其他组件,包括 Three.js 和其他东西,专门用于使用 WebVR。

我制作了一个名为NavigateButton. 下面是我的代码:

import React from 'react';
import { AppRegistry, asset, StyleSheet, Pano, Text, View, VrButton, Sphere } from 'react-vr';

export class NavigateButton extends React.Component {

    render() {
        return (
            <VrButton onClick={() => this.onNavigating()}>
                <Sphere radius={0.5} widthSegments={10} heightSegments={10} style={{ color: "red" }} />
            </VrButton>
        );
    }

    onNavigating() { // This method must throw an event
        console.log(this.props.to); 
    }
};

如果用户点击VrButton (这就像一个 HTML 5button标签,但对于 VR 内部是一个球体),则必须将一个事件引发到我调用该NavigateButton组件的位置。这是在下面的代码:

import React from 'react';
import { AppRegistry, asset, StyleSheet, Pano, Text, View, VrButton, Sphere } from 'react-vr';
import { NavigateButton } from './components/nativateButton.js';

let room = asset('360 LR/inkom_hal.jpg');

export default class MainComp extends React.Component {
    render() {
        return (
            <View>
                <Pano source={asset('360 LR/inkom_hal.jpg')} />
                <View style={{ transform: [{ translate: [20, 0, 0] }] }}>
                    <NavigateButton to="garage"></NavigateButton> 
                    <!-- and must been catch here -->
                </View>
                <View style={{ transform: [{ translate: [-7, 0, -20] }] }}>
                    <NavigateButton to="woonkamer"></NavigateButton>
                    <!-- or here -->
                </View>
            </View>
        );
    }
}

AppRegistry.registerComponent('MainComp', () => MainComp);

有可能这样做吗?我想要下面的代码来捕捉事件:

<NavigateButton to="woonkamer" onNavigate={() => this.change()}></NavigateButton>

我在互联网上搜索过,但没有找到可以帮助我的东西。

4

1 回答 1

2

以下是我和我的团队准备的如何使用 React VR 创建示例 VR 应用程序的说明:

为网页创建 VR 导览 未来应用的目录结构如下:

+-node_modules
+-static_assets
+-vr
\-.gitignore
\-.watchmanconfig
\-index.vr.js
\-package.json
\-postinstall.js
\-rn-cli-config.js

Web 应用程序的代码将位于 index.vr.js 文件中,而 static_assets 目录托管外部资源(图像、3D 模型)。您可以在此处了解有关如何开始使用 React VR 项目的更多信息。index.vr.js 文件包含以下内容:

import React from 'react';
import {
     AppRegistry,
     asset,
     StyleSheet,
     Pano,
     Text,
     View,
}
from 'react-vr';
class TMExample  extends React.Component {
     render() {
          return (
          <View> 
          <Pano source={asset('chess-world.jpg')}/>
               <Text
               style={{
                    backgroundColor:'blue',
                    padding: 0.02,
                    textAlign:'center',
                    textAlignVertical:'center',
                    fontSize: 0.8,
                    layoutOrigin: [0.5, 0.5],
                    transform: [{translate: [0, 0, -3]}],
               }}>
               hello
              </Text>
          </View>
          );
     }
};
AppRegistry.registerComponent('TMExample', () => TMExample);

使用中的 VR 组件

我们使用 React Native 打包器进行代码预处理、编译、打包和资产加载。在渲染函数中有视图、全景和文本组件。这些 React VR 组件中的每一个都带有一个样式属性来帮助控制布局。

要完成它,请检查根组件是否已向 AppRegistry.registerComponent 注册,它捆绑了应用程序并准备好运行。在我们的 React VR 项目中突出显示的下一步是编译 2 个主文件。

索引.vr.js 文件

在构造函数中,我们已经为 VR 旅游应用指明了数据。这些是场景图像、用于在具有 XYZ 坐标的场景之间切换的按钮、动画值。我们包含在 static_assets 文件夹中的所有图像。

constructor (props) {
     super(props);
    this.state =  {
      scenes: [{scene_image: 'initial.jpg', step: 1, navigations: [{step:2, translate: [0.73,-0.15,0.66], rotation: [0,36,0] }] },
               {scene_image: 'step1.jpg', step: 2, navigations: [{step:3, translate: [-0.43,-0.01,0.9], rotation: [0,140,0] }]},
               {scene_image: 'step2.jpg', step: 3, navigations: [{step:4, translate: [-0.4,0.05,-0.9], rotation: [0,0,0] }]},
               {scene_image: 'step3.jpg', step: 4, navigations: [{step:5, translate: [-0.55,-0.03,-0.8], rotation: [0,32,0] }]},
               {scene_image: 'step4.jpg', step: 5, navigations: [{step:1, translate: [0.2,-0.03,-1], rotation: [0,20,0] }]}],
      current_scene:{},
      animationWidth: 0.05,
      animationRadius: 50
      };
}

然后我们更改了将它们链接到状态的图像的输出,之前在构造函数中已指示。

<View>
    <Pano source={asset(this.state.current_scene['scene_image'])}
    style={{ 
         transform: [{translate: [0, 0, 0]}] 
    }}/>
</View>

导航按钮 在每个场景中,我们都放置了用于在游览中导航的转换按钮,从状态中获取数据。订阅 onInput 事件以传达场景之间的切换,并将其绑定到它。

<View>
        <Pano source={asset(this.state.current_scene['scene_image'])} onInput={this.onPanoInput.bind(this)}
          onLoad={this.sceneOnLoad} onLoadEnd={this.sceneOnLoadEnd}
          style={{ transform: [{translate: [0, 0, 0]}] }}/>
        {this.state.current_scene['navigations'].map(function(item,i){
              return  <Mesh  key={i}
                            style={{
                                layoutOrigin: [0.5, 0.5],
                                transform: [{translate: item['translate']},
                                            {rotateX: item['rotation'][0]},
                                            {rotateY: item['rotation'][1]},
                                            {rotateZ: item['rotation'][2]}]
                            }}
                      onInput={ e => that.onNavigationClick(item,e)}>
                              <VrButton 
                                     style={{ width: 0.15,
                                            height:0.15,
                                            borderRadius: 50,
                                            justifyContent: 'center',
                                            alignItems: 'center',
                                            borderStyle: 'solid',
                                            borderColor: '#FFFFFF80',
                                            borderWidth: 0.01
                                     }}>
                                     <VrButton
                                            style={{ width: that.state.animationWidth,


    height:that.state.animationWidth,
                                                   borderRadius: that.state.animationRadius,
                                                   backgroundColor: '#FFFFFFD9'
                                            }}>
                                     </VrButton>
      </VrButton>
    </Mesh>
          })}
      </View>
onNavigationClick(item,e){
     if(e.nativeEvent.inputEvent.eventType === "mousedown" && e.nativeEvent.inputEvent.button === 0){
          var new_scene = this.state.scenes.find(i => i['step'] === item.step);
          this.setState({current_scene: new_scene});
          postMessage({ type: "sceneChanged"})
     }
}
sceneOnLoad(){
    postMessage({ type: "sceneLoadStart"})
  }

  sceneOnLoadEnd(){
    postMessage({ type: "sceneLoadEnd"})
  }
this.sceneOnLoad = this.sceneOnLoad.bind(this);
this.sceneOnLoadEnd = this.sceneOnLoadEnd.bind(this);
this.onNavigationClick = this.onNavigationClick.bind(this);

按钮动画

下面,我们将展示导航按钮动画的代码。我们根据按钮增加原理构建了动画,应用了传统的 requestAnimationFrame。

                    this.animatePointer = this.animatePointer.bind(this);

animatePointer(){

var delta = this.state.animationWidth + 0.002;

var radius = this.state.animationRadius + 10;

if(delta >= 0.13){

delta = 0.05;

radius = 50;

}

this.setState({animationWidth: delta, animationRadius: radius})

this.frameHandle = requestAnimationFrame(this.animatePointer);

}
componentDidMount(){

this.animatePointer();

}
componentWillUnmount(){

if (this.frameHandle) {

cancelAnimationFrame(this.frameHandle);

this.frameHandle = null;

}

}

在 componentWillMount 函数中,我们已经指明了当前场景。然后我们还订阅了消息事件,用于与主线程进行数据交换。我们这样做是因为需要在一个单独的线程中开发一个 React VR 组件。

在 onMainWindowMessage 函数中,我们只处理一条带有 newCoordinates 键的消息。稍后我们将详细说明我们这样做的原因。同样,我们订阅了 onInput 事件来传达箭头转动。

componentWillMount(){
     window.addEventListener('message', this.onMainWindowMessage);
     this.setState({current_scene: this.state.scenes[0]})
}
onMainWindowMessage(e){
     switch (e.data.type) {
          case 'newCoordinates':
               var scene_navigation = this.state.current_scene.navigations[0];
               this.state.current_scene.navigations[0]['translate'] = [e.data.coordinates.x,e.data.coordinates.y,e.data.coordinates.z]
               this.forceUpdate();
          break;
     default:
          return;
     }
}
<Pano source={asset(this.state.current_scene['scene_image'])} onInput={this.onPanoInput.bind(this)}
         style={{ transform: [{translate: [0, 0, 0]}] }}/>
rotatePointer(nativeEvent){
     switch (nativeEvent.keyCode) {
           case 38:
                 this.state.current_scene.navigations[0]['rotation'][1] += 4;
           break;
           case 39:
                 this.state.current_scene.navigations[0]['rotation'][0] += 4;
           break;
           case 40:
                 this.state.current_scene.navigations[0]['rotation'][2] += 4;
           break;
           default:
                 return;
     }
     this.forceUpdate();
}

箭头转动是用↑→↓ alt 键完成的,分别用于 YXZ 轴。

在 Github HERE 上查看并下载整个 index.vr.js 文件。

客户端.js 文件

进一步了解我们的 React VR 虚拟现实 Web 应用程序示例,我们已将以下代码添加到 init 函数中。目标是处理 ondblclick、onmousewheel 和消息事件,后者在渲染线程中进行消息交换。此外,我们保留了指向 vr 和 vr.player._camera 对象的链接。

window.playerCamera = vr.player._camera;
window.vr = vr;
window.ondblclick= onRendererDoubleClick;
window.onmousewheel = onRendererMouseWheel;
vr.rootView.context.worker.addEventListener('message', onVRMessage);

我们引入了 onVRMessage 函数,用于在场景变化时恢复默认缩放。此外,我们还添加了场景变化时的加载器。

function onVRMessage(e) {
     switch (e.data.type) {
          case 'sceneChanged':
               if (window.playerCamera.zoom != 1) {
                    window.playerCamera.zoom = 1;
                    window.playerCamera.updateProjectionMatrix();
               }
          break;
          case 'sceneLoadStart':
             document.getElementById('loader').style.display = 'block';
          break;
          case 'sceneLoadEnd':
             document.getElementById('loader').style.display = 'none';
          break;
          default:
               return;
     }
}

onRendererDoubleClick用于计算 3D 坐标并向 vr 组件发送消息以更改箭头坐标的功能。该get3DPoint函数是为我们的 Web VR 应用程序定制的,如下所示:

function onRendererDoubleClick(){
    var x  = 2 * (event.x / window.innerWidth) - 1;
    var y = 1 - 2 * ( event.y / window.innerHeight );
    var coordinates = get3DPoint(window.playerCamera, x, y);
    vr.rootView.context.worker.postMessage({ type: "newCoordinates", coordinates: coordinates });
}

切换到鼠标滚轮

我们使用了onRendererMouseWheel将缩放切换到鼠标滚轮的功能。

function onRendererMouseWheel(){
     if (event.deltaY > 0 ){
          if(window.playerCamera.zoom  > 1) {
               window.playerCamera.zoom -= 0.1;
               window.playerCamera.updateProjectionMatrix();
          }
     }
     else {
          if(window.playerCamera.zoom < 3) {
               window.playerCamera.zoom += 0.1;
               window.playerCamera.updateProjectionMatrix();
          }
     }
}

导出坐标

然后我们使用 Three.js 来处理 3D 图形。在这个文件中,我们只传递了一个函数来将屏幕坐标导出到世界坐标。

import * as THREE from 'three';
export function get3DPoint(camera,x,y){
    var mousePosition = new THREE.Vector3(x, y, 0.5);
    mousePosition.unproject(camera);
    var dir = mousePosition.sub(camera.position).normalize();
    return dir;

}

在 Github HERE 上查看并下载整个 client.js 文件。可能不需要解释 cameraHelper.js 文件是如何工作的,因为它很简单,您也可以下载它。

此外,如果您对相似的项目估算或有关 ReactVR 开发的相同附加技术细节感兴趣 - 您可以在此处找到一些信息:

于 2017-04-19T13:46:34.820 回答