9 posts tagged

React

Introduction to React VR , Part 2

Shay Keinan

Read the first part

We’ll begin by installing the react-vr-cli.

npm install -g react-vr-cli

Next, we are going to initialize the project. This will install all of the dependancies that we need to run the project.

react-vr init WelcomeToVR

cd WelcomeToVR && npm start

Facebook already created a very basic ‘hello world’ project which they call welcome to VR. After installation you should see the following screen:

After the init we get the following file structure:

__tests__
node_modules
static_assets
vr
  index.html
  client.js
index.vr.js

All the static assets are located in the static_assets folder. The entry point for any VR application is the index.vr.js file. In the VR folder there is always a file called client.js  which is  used for the application configuration and communicating with the Three.js scene.

Let’s take a look at index.vr.js. This is the entry point for our application. We have a basic React component that contains two children:  Pano for the panoramic image of the room and Text for the word “hello”. On the bottom we register the WelcomeToVr component just like in React.

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

Let’s begin building our own application. The app that we are going to build together will represent our solar system planets.

This is what the application is going to look like. We can see a planet with some text, there is a menu on the right to change a planet, and some additional info about the planet on our left.

Final application demo

3D coordinates and Transformations

To know where to place objects and how to move around the scene we need to be familiar with the coordinate system.

The coordinate system is the same as OpenGL coordinates meaning that y positive is up, and we use the right hand rule for rotation.

We can apply transformations on any component, just like we do with CSS The syntax is a bit different than classic CSS but should be familiar to those who code in React Native. If I want to place an object anywhere other than [0,0,0] I use translate. If I want to change its size I use scale. And if I want to rotate it along a certain axis I use rotate.

Just like React Native, React VR provides a set of basic primitives used to construct user interface. This is a powerful feature of React VR: developers can use the same styling and layout system across web, React Native, and VR, which opens the doors to directly sharing layout styles across these platforms.

View is the most fundamental component for building a UI. It is a container that supports layout with flexbox, styling, and touch handling. View maps directly to the native view equivalent on whatever platform React Native is running on, whether that is a UIView, <div>, or android.view. View is designed to be nested inside other views and can have zero to many children of any type.

Text is a React component for displaying text. It supports nesting and styling.

Image is a react component for displaying different types of images. Keep in mind that you must manually specify the dimensions of your image in meters and it is not possible to predict the intended dimensions from the pixel width and height of the image.

Pano is an image projected onto a sphere that fully surrounds the viewer. It is a Sphere of 1000m with a center located at the local transform origin at [0,0,0]. You can create pano compatible images by using a special 360 camera or even with your phone. Cube map images are also supported in Pano, you can specify a source url as an array of 6 individual images.

So lets add a Pano component to our index.vr.js, we need to provide an asset to the source property.

Note that we are using a utility from react VR called asset. This allows us to load assets directly from the static_assets folder I showed you before. Also note that this is not just any jpeg but rather one that supports 3D.

render() {
  return (
    <View>
      <Pano source={asset('stars.jpg')}/>
    </View>
  );
}

This is the result, our sphere is the cosmos. The image surrounds us.

Styling with Flex box

React VR makes use of a Flexbox style layout algorithm to automatically position components and their children. The library used is CSSLayout, the syntax is more or less identical to the web flexbox.

As you can imagine, using flexbox to layout content on a 2D plane is not optimal for a 3D experience. So it’s a common practice to layout the content on a cylindrical plane. This will give us a ‘2.5D’ feel.

Interactions

In VR we interact with objects in a different way than the web. We don’t necessarily click on something, it may be enough that we look at an object for a significant amount of time to trigger an event.

Besides looking we can also press a controller to trigger a reaction.

To make our scene interactive we are going to use react’s VrButton — a helper component that manages the interaction state machine. By default, VrButton has no appearance and will only act as a wrapper to capture events, but it can be styled in the same ways as a View.

When pressing a button in our menu, the handleClick function will save the planet name in the currentPlanet variable.

menu() {
  return (
    <View
      billboarding={'on'}
      style={styles.menu}>
        { 
          Object.keys(planets).map((planet) => (
            <VrButton key={`button-${planet}`}
              onClick={() => this.handleClick(planet)}>
              <View style={styles.planetBtn}>
                <Text style={styles.planetBtnLabel}>{planet}</Text>
              </View>
            </VrButton>
          )) 
        }
    </View>
  );
}

Let’s take a look at our menu again. Now when we press a button we are updating the currentPlanet variable in our state, and as a result the header component updates.

Loading models

There are many types of model formats available out there like obj, stl, or collada. They may come with a built-in animation, or with material and texture. At the moment, React VR supports the Wavefront OBJ file format only.

React VR provides a component for loading models, called Model. Let’s load Earth.

Here is our planet declaration. The lit property specifies if the model will be affected by lights. The model’s ability to define a style enables us to apply different transformations such as   scaling, rotating, or translating.

<Model
  source={{obj: asset(`models/Earth.obj`)}}
  texture={asset(`textures/Earth.png`)}
  lit={true}
  style={styles.planet}
/>

Here is the loaded model. By changing the current planet we’re causing a new model to load every time.

Animations

To make our scene more interesting we can add animation to objects and layouts. There are two ways to create animations in React VR : using the Animated library, or manually using requestAnimationFrame.

Below is an example of requestAnimationFrame . What I want to do here is rotate a planet. The rotate function in this example is called in a loop hopefully at least 60 frames per second. On each call we are advancing the rotation by a delta.

state = {
  rotation: 0
};
 
componentDidMount() { this.rotate(); }
 
// planet rotate animation
rotate() {
  const now = Date.now();
  const delta = now - this.lastUpdate;
 
  this.time++;
 
  this.lastUpdate = now;
  this.setState({
    rotation: this.state.rotation + delta / 150
  });
  this.frameHandle = requestAnimationFrame(this.rotate);
}

This is the result, we can see the planet rotating. If we switch planets the new model will still rotate around itself.

Rotating animation

Lets see an example of how to use the Animated library for animations. When a button is pressed we want to make it look like it bounces a bit, and also when a planet appears we will give it a nice little bounce. In the Animated library this effect is called spring.

state = {
  bounceValue: new Animated.Value(1)
};

// bounce animation
bounce({initial, toValue, friction = 1.5}) {
  value.setValue(initial);
 
  Animated.spring(
    this.state.bounceValue,
    {toValue, friction}
  ).start();
}

Native modules

If React VR doesn’t support a feature that you need, you can build it yourself using native modules.

A lot of stuff that exists in Three.js has not yet been implemented in React VR, for example there are dozens of geometries in Three.js of which only sphere, box and cylinder exist in React VR.

This Tetrahedron geometry is supported in Three.js but not in React VR. So lets create a native module, let’s extend it for React.

Tetrahedron geometry

I need to do 2 things . First I create a module and in its constructor which I receive the Three.js scene. From this point the Three.js scene is open to me.

The rows within the add functions are all Three.js code. I define a tetrahedron geometry, a meshLambert material and a new mesh that I’m adding to the actual Three.js scene.

import { Module } from 'react-vr-web';
import * as THREE from 'three';
 
export default class AsteroidsModule extends Module {
  constructor(scene) {
    // The name of the module in NativeModules
    super('Asteroids');
 
    this.scene = scene;
  }
 
  add() {
    const color = 0x7F492A;
    const geometry = new THREE.TetrahedronGeometry(10, 1);
    const material = new THREE.MeshLambertMaterial({ color });
    const mesh = new THREE.Mesh(geometry, material);
 
    this.scene.add(mesh);
  }
 }

The second thing I have to do is go to my client.js file, and place my native module in the native modules array. I also make sure that my module is rendered.

import {VRInstance} from 'react-vr-web';
import AsteroidsModule from '../asteroidsModule';
import * as THREE from 'three';
 
function init(bundle, parent, options) {
  const scene = new THREE.Scene();
  const Asteroids = new AsteroidsModule(scene);
  const vr = new VRInstance(bundle, 'solarSystem', parent, {
    // Add custom options here
    nativeModules: [ Asteroids ],
    scene,
    ...options,
  });
  
  vr.render = function() {
    Asteroids.render();
  };
   
  // Begin the animation loop
  vr.start();
  
  return vr;
}

Here is my tetrahedron geometry in react VR. These tetrahedrons are going to be my asteroids.

 Tetrahedron geometry

Here is the final application: we are inside a Pano where we loaded an image of stars. We see the header with two texts inside. Below is a loaded model, the planet surrounded by asteroids that we created by using native modules.

On the right is a Menu, a click on the menu switches to a different planet with a bounce. The menu button also has a spring to it. On the left we have an info view. Both the menu and the info have a billboard property that is on and as a result those views always face us.

Final ‘Solar System’ application

You can find the entire project on GitHub. Feel free to use it.

React VR is relatively new, time will tell if the community is willing to adopt it. React VR is a quick and easy way to build a 3D application. I’ll be happy if you share your future applications and insights with me.

Thanks for your time!

Introduction to React VR , Part 1

Shay Keinan

Virtual reality is being used in many industries. Besides games, virtual reality is being used in many fields like medicine, education, and movies. Because of its ability to completely immerse you in a scene the possibilities are endless.

Before diving into code I want to talk to you about virtual reality concepts, things that we, as developers, must know if we want to build a virtual reality application.

So what is virtual reality in a nutshell? Visual virtual reality is made up of 2 things: stereoscopic imaging and movement tracking.

Lets look at these 2 images. Are they identical?

They seem identical but if you look closely you can see a difference.

Stereoscopic images are based on how the human brain works: it takes two images that show the same content but from a slightly different point of view. The offset of these images corresponds to the distance between our eyes, this distance is called inter-pupillary distance, IPD for short. In this way we simulate the way we see the world naturally, and it gives us the perception of 3D depth.

Headset lenses are an integral part of the virtual reality experience. Why do we use headset lenses Because they position the images on the screen at the exact distance that they need to be to get the desired effect. VR lenses are thick so they cause a distortion. The square that you see on the left looks caved in through the lenses. The outcome looks something like what we see on the right.

To compensate for this, we give images that are rounded out. On the left you can see a compensated image before the lenses, and on the right is the final desired image.

To sum up stereoscopic imaging: by showing two slightly different images to each eye, using special lenses, we get the effect of depth.

Besides stereoscopic imaging the second thing we need to complete the illusion of virtual space is to track the movement of our body. All VR devices track head movement so we can look around. Some devices, the more expensive ones, track body movements, so we can move around. Of course the more tracking sensors that you have, the better the illusion of reality.

On April this year, Facebook announced the launch of React VR, a new JavaScript framework, based on Three.js and React Native.

React VR allows developers to build virtual reality experiences with the help of JavaScript. As the name implies, React VR uses the same concepts as Facebook’s existing React framework. Just like with React for standard web apps, VR developers can now use the same declarative model to write apps for their 360-degree experiences.

Just like in animation, VR apps need to be rendered at 60 frames per second, React Native has solved many of the issues that usually make this hard to do with a JavaScript application. It’s important to know that React VR is based on React Native and Three.js. Most of the components that we use are React Native components. The 3D rendering engine is the Three.js rendering engine.

Three.js is a cross-browser JavaScript library used to create and display animated 3D graphics.

In the next part, we are going to cover React’s VR basic components and start to write an application from scratch. If you can’t wait and want to move on the entire tutorial is available here:

Dependency Injection in React With Provider Pattern

Adam Klein

The Provider pattern introduces a way to access some global object (service) in deeply nested components. If you’ve been using React for a while, you probably used it already. In fact, many popular libraries use this pattern: react-router, react-redux, mobx-react, react-intl, react-dnd, or styled-components.

A provider that wraps a component tree:

<Provider>
  <Component/>
</Provider>

An injector as higher-order function:

export default withProvider(Component);

You can also relate to this pattern as dependency injection as a mechanism of exposing a global dependency to nested components on demand, somewhat similar to Angular’s mechanism, but not as a general definition of dependency injection in software development.

Let’s say we want to create a customizable theme for our app and use it in any component we’d like:

const SendButton = () => (
  <button style={{ color: theme.primaryColor }}>Send</button>
);

Somehow we need the theme instance to be available in this component. Moreover, we want to make it reactive, meaning whenever we change the theme, we want the component to update.

Let’s start by creating a top-level component that will hold the theme, and pass it down using React context:

export class ThemeProvider extends React.Component {
  state = {
    primaryColor: '#448866'
  };

  static childContextTypes = {...};

  getChildContext() {
    return {
      getTheme: () => this.state
    };
  }

  render() {
    return <span>{ this.props.children }</span>;
  }  
};

Next, we’ll put the provider in the top-level of our app, to make the theme available everywhere:

const App = () => (
  <ThemeProvider>
    ...
  </ThemeProvider>
);

Now we need a way to access the theme from child components. We could do it directly from the context, but there are a few drawbacks to this:

  • Using context directly is not the best practice;
  • We are coupling our components to ThemeProvider;
  • We can’t use React’s lifecycle hooks to react to changes in theme.

So instead we use an injector:

function withTheme(WrappedComponent) {
  const Wrapper = (props, { getTheme }) => (
    <WrappedComponent
      getTheme={ getTheme }
      { ...props }
    />
  );

  Wrapper.contextTypes = {...};

  return Wrapper;
}

The injector is a higher-order component (HOC) that takes the theme from the context and passes it down as props. Now to use the theme inside a component, you wrap it with the withTheme HOC:

const Component = (props) => (
  <div style={{ color: props.getTheme().primaryColor }}>
    This is the primary color
  </div>
);

export default withTheme(Component);

Reusing a service

Let’s now say we want to have two different themes side by side on our page. Since we are using context, we just need to put two ThemeProvider’s and each one of them will supply a different theme instance for its component subtree:

<div>
  Theme 1:
  <ThemeProvider>
     ... // components using theme 1
  </ThemeProvider>

  Theme 2:
  <ThemeProvider>
    ... // components using theme 2
  </ThemeProvider>
</div>

See full source code and demo on StackBlitz

When to use the pattern

Usually you can use generic store mechanisms like React and MobX to access global resources, but in some cases Redux and MobX are not good enough:

  • You are creating a generic component that should serve any architecture (for example if you create styled-components and want to supply a theme);
  • You need the services to be reusable on the page, and be able to declare a service per component tree;
  • You need to save large amounts of data that you don’t want on the store.

Other (bad) approaches

You could pass the theme down the component tree as a prop. In this case all the component’s ancestors have to be aware of the theme and pass it down which creates coupling, and is not a very productive way of writing code.

You could also export a theme object and import it wherever you need. In this case it won’t be reactive out of the box, meaning if we change the theme it won’t be reflected in the components that use it. Also, if we want to support showing two themes on the same page we can’t do that with a global singleton.

ReactNext 2017 Highlights

Ilya Gelman

Last September we held ReactNext 2017, our annual React conference in Tel Aviv. This year we had an honor to welcome over 700 attendees from 15 countries for a full day focused on React and React Native with 20 speakers in two tracks.

We have published the videos of the presentations on the conference YouTube channel (the link is at the end of the post) and wanted to highlight some of the talks.

The biggest piece of news in React ecosystem this year is the release of React 16 with a completely rewritten reconciliation engine called Fiber (and a new license). Ari Lerner covered all the awesome features that came with the new release (and virtually no breaking changes).

Ari Lerner goes through new features in React 16

One of the most interesting presentations was probably Michel Weststrate’s talk about mobx-state-tree, his opinionated state container for applications. Many people will now consider it as a serious alternative to Redux.

Michel Weststrate released mobx-state-tree

Speaking about state management, Boris Dinkevich gave an advanced talk on some of the most powerful—yet underestimated—features of Redux, middleware. Must watch if you use Redux in your application.

Boris Dinkevich teaches Redux middleware

Many companies that develop web and mobile applications using React and React Native want to reuse as much code between platforms as possible. Peggy Rayzis shared her practical experience on how to reuse more code with universal components.

Peggy Rayzis talks about universal components

Finally we’d like to highlight a presentation of a great tool that will save countless hours to companies that seriously use React Native. Rotem Mizrachi-Meidan talked about Detox, end-to-end testing and automation library for mobile apps developed by Wix.

Rotem Mizrachi-Meidan introduces Detox

We had a great time organizing and attending the conference and hope all the guests, speakers, and sponsors enjoyed it as much as we did. You can find the rest of the videos on the conference YouTube channel.

Follow ReactNext on Facebook and Twitter for more updates, photos from the event, and future announcements.

Conferences   React   ReactNext   Video
Earlier Ctrl + ↓