- Home
- >
- Mobile apps development
- >
- How to make React VR Apps
VR web apps can be built with React VR apps. WebVR is an experimental API enabling the creation and viewing of VR experiences in your browser. This new technology enables you to grant access to virtual reality regardless of the devices at hand.
The only thing you need to make a React VR app is a headset and a compatible browser. If you are just viewing a web VR application then you don’t need even for a headset.
React VR is a great framework to build VR websites or apps on JavaScript. It utilizes the same design as React Native and lets you make virtual reality tours and under interfaces with the provided components.

How to make a VR app
Technical Requirements
Before getting started with React VR, You required a proper setup for the dependencies that will be used to build and manage a React VR app. These are Node.js and the React VR CLI.
You need to install Node.js, ensure that the latest version is used. If not, do the following:
- MacOS: Install Node.js using Homebrew
- Windows: Install it from the nodejs.org
- Linux: Use sudo apt-get install nodejs command
Then you have to install the React VR CLI using npm:
npm install -g react-vr-cli.
Getting Started
Navigate to the directory where you wish to create a new project and run the react-vr init MyFirstReactVR
command.
Change the directory name to something like MyFirstReactVR and run npm start
.
Click and try to drag your cursor around. Also, in browsers that support WebVR, you’ll be able to explore this VR environment with a headset in full virtual reality mode.
How to develop VR apps
Now let’s make a VR tour with a few scenes and navigations in them. First of all, you need to prepare the architecture. In this example, we will make buttons within each scene for navigations and declare this in the constructor of a React component named index.js.vr:
constructor(props) {
super(props);
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]
}]
}]
}
Also, declare current_scene in the state in the constructor:
constructor(props) {
this.state = {
...
current_scene: {}
...
}
For rendering, change the render() method, as seen below:
render() {
return ( < View >
< Pano source = {
asset(this.state.current_scene['scene_image'])
}
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]
}]
}
} >
< VrButton
style = {
{
width: 0.15,
height: 0.15,
borderRadius: 50,
backgroundColor: 'blue'
}
} >
< /VrButton> < /Mesh>
})
} < /View>
)
}
The last step to make this work is to set the current_scene state to the first element of the scenes array componentWillMount function:
componentWillMount() {
this.setState({
current_scene: this.state.scenes[0]
});
}
The result should be like this on screen:
Now add a simple animation for the button and implement the logic to navigate between scenes. First of all, for navigation, you need to subscribe to the onInput event for our Mesh element, bind this function to this in constructor, and implement it:
...
constructor(props) {
...
this.onNavigationClick = this.onNavigationClick.bind(this);
...
}
...
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
});
}
}
...
render() {
var that = this;
... < Mesh key = {
i
}
...
onInput = {
e => that.onNavigationClick(item, e)
}
.... >
...
}
Then added a simple animation try it out. For this, you have added one more button inside the existing one in the render method and changed the size of it. Use native JS requestAnimationFrame function:
const DEFAULT_ANIMATION_BUTTON_RADIUS = 50;
const DEFAULT_ANIMATION_BUTTON_SIZE = 0.05;
constructor(props) {
...
this.state = {
...
animationWidth: DEFAULT_ANIMATION_BUTTON_SIZE,
animationRadius: DEFAULT_ANIMATION_BUTTON_RADIUS
...
}
...
this.animatePointer = this.animatePointer.bind(this);
}
...
componentWillUnmount() {
if (this.frameHandle) {
cancelAnimationFrame(this.frameHandle);
this.frameHandle = null;
}
}
componentDidMount() {
this.animatePointer();
}
animatePointer() {
var delta = this.state.animationWidth + 0.002;
var radius = this.state.animationRadius + 10;
if (delta >= 0.13) {
delta = DEFAULT_ANIMATION_BUTTON_SIZE;
radius = DEFAULT_ANIMATION_BUTTON_RADIUS;
}
this.setState({
animationWidth: delta,
animationRadius: radius
})
this.frameHandle = requestAnimationFrame(this.animatePointer);
}
...
render() {
... < 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>
...
}
Now let’s implement manipulations with the animated button and its rotation. It will rotate the button on an X-Y-Z axis. To do so, you need to subscribe to the onInput event for the Pano component and change the rotation via arrow-up, arrow-right, and arrow-down buttons.
The last thing is to implement messaging of the VR thread and Main thread to exchange data. Below is the subscription code on receiving messages and posting a message when a scene changes or an image starts/ends loading.
componentWillMount() {
window.addEventListener('message', this.onMainWindowMessage);
...
}
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;
}
}
onNavigationClick(item, e) {
...
postMessage({
type: "sceneChanged"
})
this.state.animationWidth = DEFAULT_ANIMATION_BUTTON_SIZE;
this.state.animationRadius = DEFAULT_ANIMATION_BUTTON_RADIUS;
this.animatePointer();
...
}
sceneOnLoad() {
postMessage({
type: "sceneLoadStart"
})
}
sceneOnLoadEnd() {
postMessage({
type: "sceneLoadEnd"
})
}
render() {
... < Pano...
onLoad = {
this.sceneOnLoad
}
onLoadEnd = {
this.sceneOnLoadEnd
}
... / >
}
In client.js, implement a zoom with the mouse wheel and a position change with a double-click. you need to store the VR instance and VRcamera instance to implement the logic above.
function init(bundle, parent, options) {
const vr = new VRInstance(bundle, 'TMExample', parent, {
// Add custom options here
...options,
});
vr.render = function() {
// Any custom behavior you want to perform on each frame goes here
};
// Begin the animation loop
vr.start();
window.playerCamera = vr.player._camera;
window.vr = vr;
return vr;
Then subscribe to ondblclick and onmousewheel and implement the zoom and change position logic.
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
});
}
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();
}
}
}
get3DPoint is your custom function to transform screen coordinates to world coordinates using Three.js, implemented in cameraHelper.js.
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;
}
Sometimes, loading scene images takes time. thus, you have implemented the loader to show this process. In index.html, add a loader and CSS based on this w3cschool example.
<style>
body { margin: 0; }
#loader {
position: absolute;
left: 50%;
top: 50%;
z-index: 1;
width: 150px;
height: 150px;
margin: -75px 0 0 -75px;
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.animate-bottom {
position: relative;
-webkit-animation-name: animatebottom;
-webkit-animation-duration: 1s;
animation-name: animatebottom;
animation-duration: 1s
}
@-webkit-keyframes animatebottom {
from { bottom:-100px; opacity:0 }
to { bottom:0px; opacity:1 }
}
@keyframes animatebottom {
from{ bottom:-100px; opacity:0 }
to{ bottom:0; opacity:1 }
}
#myDiv {
display: none;
text-align: center;
}
</style>
<body>
<div id='content' style="width:100%; height:100%">
<div id="loader"></div>
</div>
<script src="https://codersera.com/blog/how-to-make-react-vr-apps-2/./client.bundle?platform=vr"></script>
<script>
ReactVR.init('../index.vr.bundle?platform=vr&dev=true', document.getElementById('content'));
</script>
</body>
And don’t forget message in client.js from the VR thread to enable/disable animation:
function init(bundle, parent, options) {
...
vr.rootView.context.worker.addEventListener('message', 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;
}
}
Check your index.html or client.js.
InApps – Top App development company in Vietnam
When it comes to coding an app, you have three options: you can either hire an app development agency to build and design your product. You can create your own internal design and development team, or you can build the app yourself.
InApps is a leading application development company in Vietnam. We have extensive experience in developing different applications in all industries. Please view Our portfolio. As long as you have an idea, we will help you turn it into reality. Share your ideas with us now.
List of Keywords users find our article on Google
[sociallocker id=”2721″]
react vr |
create vr app |
react native vr |
how to develop vr apps |
react setstate |
dna vr |
nodejs org |
reactvr |
w3cschool |
react vr game |
react native share whatsapp |
this setstate |
react constructor |
react vr tutorial |
build a virtual reality application with react learning |
vr bundle |
border radius react native |
componentwillmount |
react native transform |
setstate react |
0.15 * 50 |
make vr app |
react js vr |
react native zoom |
npm threejs |
react-native-vision-camera |
headset cartoon |
react native vision camera |
react virtual reality |
react native virtual reality |
constructor custom cursor |
react-vr |
.map is not a function react |
react native position absolute |
vr react native |
this.setstate |
infinite outsourcing solutions |
react native camera |
webkit postmessage |
react native templates |
var headset |
react native border |
react recruitment |
javascript display none |
three js react native |
react native template design |
drag react native |
front end loader hire |
index vr |
loader react |
saas loader |
360 spin images for ecommerce |
react native app development |
react native mobile app development |
react native app development company |
how to make a vr app |
webvr portfolio |
react vr documentation |
build a vr app |
create vr apps |
event check-in software for radius |
cli elearning |
how to create vr apps |
offshore it translator |
react style display none |
how to develop vr application |
onn headphones |
react native vr app |
zoom react native |
react native 0.66 |
three.js mesh |
react-native-branch |
react-icons npm |
virtual reality react native |
oculus app labs |
adobe xd component states |
react native branch |
offshore translate |
react icons npm |
react native animated event |
react native stories |
building vr apps |
emotion react npm |
react native center text |
camerax sample |
react-native border radius |
npm normalize |
style-loader npm |
adobe xd map |
react a-frame |
react native content loader |
position absolute in react native |
vr apps |
adobe xd map template |
border react native |
react native loader |
schedule component react |
three js mesh |
zindex react native |
virtual office ho chi minh |
vr travel case |
js vector3 |
nodejs get parent directory |
react image rotate |
react native align to bottom |
react native border radius |
adobe xd to react native |
react native animated button |
react native bind |
react native drag |
react native rotate view |
react-native-touch-id |
.map react |
case logic camera |
opacity react native |
react-native-camera |
three js group rotation |
borderradius react native |
constructor react |
mousepose for windows |
used index vr |
ecommerce xd template |
react rotate image |
react setstate array |
webkit zoom |
3498db |
react web worker |
react-content-loader |
let’s play vr |
react array state |
react native translate |
react onmousewheel |
requestanimationframe react native |
bind this react |
display:none javascript |
hire webkit developers |
js addeventlistener |
react addlistener |
react native design templates |
react native range |
react native sketch |
react window infinite loader |
threejs loaders |
vector3 three js |
#3498db |
node js constructor |
react double click |
react get mouse position |
react native animation loop |
react native rotate animation |
react native zindex |
react-translate |
transform.rotation |
vietnam coordinates |
wheel game crypto software development |
react justify content |
rotate arrow css |
three js react |
translate react native |
z index in react native |
display none javascript |
react loader |
case logic high zoom camera case |
ondblclick |
portfolio website using react |
react native free template |
react set state |
react sketch app |
react-phone-number-input |
vr top of vietnam |
adobe xd css export |
how to add border in react native |
in app messaging react native |
loader in react js example |
react rotation |
this.state is null |
z-index react native |
margin top in react js |
react disable zoom |
react state array |
react native game development |
vr app development |
react native mobile development |
vr development |
hire dedicated react native developers |
react js mobile app development |
hire react native development services |
react native mobile app development company |
react native application development services |
react native mobile app development services |
react native development services |
[/sociallocker]
Let’s create the next big thing together!
Coming together is a beginning. Keeping together is progress. Working together is success.