Today, we will see how to handle images and add some animations to our project. We will be using the same project as the last episode.

Images

There is a React component for adding images. The image can be in our computer or we can pass a URL as well.

Let's add the react logo as an image. Our source will be the wikipedia image URL. We have added it, but now we need to check the image position.

Let's put the image near our astronaut. Just like in the last episode, we can play with the translate transformation.

We can use in our URL /?hotreload, and it hot-reloads the website for us.

Let's decrease the size a bit , by applying a scale transformation by 0.5. Now the image looks like what I wanted it to.

Animations

Very similar to how React Native does it, we have animations on React VR. We just need to import Animated, create an Animated.Value and play with it.

Let's start with the value in our constructor, as 0.

constructor(props) {
  super(props);
  this.animatedValue = new Animated.Value(0);
}

And our Image will now be an Animated.Image. Using this, we can use animations in the image.

<Animated.Image
          style={{
            width: 200,
            height: 200,
            transform: [
              {
                translate: [-250, 250, -500]
              },
              { scale: 0.5 }
            ]
          }}
          source={{
            uri: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1200px-React-icon.svg.png"
          }}
        />

Now, we will use a translateY transformation. It changes the image in the Y axis. The value of this will be the this.animatedValue, which we created.

<Animated.Image
        style={{
          width: 200,
          height: 200,
          transform: [
            {
              translate: [-250, 250, -500]
            },
            { translateY: this.animatedValue }
          ]
        }}
        source={{
          uri: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1200px-React-icon.svg.png"
        }}
      />

We will start the animation in our componentDidMount method. We will use an Animation of the type timing. There are other types of animations, timing is one of the easiest. In the timing we have an animation duration, the value it will change, and the delay to start.

Delay and duration are in milliseconds. Our delay will be 3 seconds, and the animation duration will be 500 milliseconds, and we will change the value from 0(our initial value) to 100. So, it will change the position in the Y axis from 0 to 100. To start the animation, we have the method start. In the method start we can pass a function to know when it finishes. In our case, we will just have a console.log on it.

  componentDidMount() {
    console.log("componentDidMount");
    Animated.timing(this.animatedValue, {
      duration: 500,
      toValue: 100,
      delay: 3000
    }).start(() => {
      console.log("Animation finished");
    });
  }

This should be enough for this simple animation.

Here is our entire code.

import React from "react";
import {
  AppRegistry,
  asset,
  Pano,
  Text,
  View,
  Model,
  Image,
  Animated
} from "react-vr";

export default class WelcomeToVR extends React.Component {
  constructor(props) {
    super(props);
    this.animatedValue = new Animated.Value(0);
  }

  componentDidMount() {
    console.log("componentDidMount");
    this.animatedValue.setValue(0);
    Animated.timing(this.animatedValue, {
      duration: 500,
      toValue: 100,
      delay: 3000
    }).start(() => {
      console.log("Animation finished");
    });
  }

  render() {
    return (
      <View>
        <Pano source={asset("eiffel_tower.jpg")} />
        <Model
          source={{
            obj: asset("astronaut.obj"),
            mtl: asset("astronaut.mtl"),
            texture: asset("Astronaut_BaseColor.jpg")
          }}
          style={{
            transform: [
              {
                translate: [10, -10, -50]
              },
              { scale: 3 }
            ]
          }}
        />
        <Animated.Image
          style={{
            width: 200,
            height: 200,
            transform: [
              {
                translate: [-250, 250, -500]
              },
              { translateY: this.animatedValue }
            ]
          }}
          source={{
            uri: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1200px-React-icon.svg.png"
          }}
        />
        <Text
          style={{
            backgroundColor: "#777879",
            fontSize: 15,
            fontWeight: "100",
            layoutOrigin: [0.5, 0.5],
            paddingLeft: 3.2,
            marginLeft: 5,
            paddingRight: 0.2,
            textAlign: "center",
            textAlignVertical: "center",
            transform: [{ translate: [60, 300, -700] }]
          }}
        >
          hello react-vr
        </Text>
      </View>
    );
  }
}

AppRegistry.registerComponent("WelcomeToVR", () => WelcomeToVR);

Let's reload the page and see the animation running. It works! We can even open the console and see the console.log we did.

We can try to use another type of animation. Spring. It follows the animation of a physics spring. To do that, we can change timing to spring. Then, we can play with two other attributes: friction and tension.

Tension controls the speed of spring. Friction controls the bounciness. Let's start with a friction 3 and we will change it later to 1 and you will see the difference. I will also increase the Y value to 150 and the duration to 800 milliseconds.

  componentDidMount() {
    console.log("componentDidMount");
    Animated.spring(this.animatedValue, {
      duration: 800,
      tension: 5,
      friction: 3,
      toValue: 150,
    }).start(() => {
      console.log("Animation finished");
    });
  }

So, now it does the translate animation, and it follows the spring physics model. Let's change the friction to 1. Now, we can see it bounces more at the end. The delay doesn't work here. We can remove it. Now everything works, and we have another type of animation! Cool!

Another cool thing we can do is compose animations. As we can't use the delay in the spring animation, we could do a timing animation before the spring animation, move up the image a bit. After that, we could do a spring animation and probably move the object more. Let's do that.

To compose animations, we can use Animated.sequence. It receives an array of animations. We will use the same value for both animations this.animatedValue.

  componentDidMount() {
    console.log("componentDidMount");
    Animated.sequence([
      Animated.timing(this.animatedValue, {
        duration: 800,
        toValue: 150,
        delay: 3000
      }),
      Animated.spring(this.animatedValue, {
        duration: 800,
        friction: 1,
        tension: 5,
        toValue: 250
      })
    ]).start(() => {
      console.log("Sequence of two animations finished.");
    });
  }

I will use a 3 seconds delay in the timing animation. The value will be up to 150. After that, the spring animation will start, and it will go up to 250, moving the Y. When we start, it will execute these two animations in order. Executing... And we can see it has executed the two animations! Awesome!

Summary

Today we saw how to handle images in React VR and how to make them animated. We will see more animations in future episodes, and how to let the user control these animations.

Resources