Waldo sessions now support scripting! – Learn more
App Development

React Native Transforms: Learn by Example

Siddhant Varma
Siddhant Varma
React Native Transforms: Learn by Example
October 4, 2021
10
min read

If you come from a web background, you know that CSS transforms are extremely helpful. You can use them to create animations and visually appealing UI elements. In fact, a lot of modern web design is inspired by transforms.

React Native comes with default support for transforms. It’s very similar to how transforms work on the web and can be used in a number of scenarios. In this roundup, I’ll break down transforms for you. You’ll understand what they are and how they work, and eventually you’ll learn how to use them in your React Native project.

Transforms in Theory With Examples

The transform property is an umbrella term for a list of properties that can be used to rotate, scale, and translate an object on the screen. In other words, it’s a combination of some properties that you can use to change the orientation and position of an object.

However, before you can use these properties, you must understand how they work. There’s a little math and theory involved, but I can assure you it’s child’s play! Let’s get started by creating an empty React Native project where we can see code and some examples.

Create React Native Project

I am using Expo CLI to quickly get started with creating a React Native project. If you’re new to Expo, you can get started with it here.

Create a new Expo app by running:


expo init react-native-transforms

Then, open the Expo project by running:

cd react-native-transforms && expo start

Now you have an empty React Native project up and running. Next, let’s add some boilerplate code.

Add Boilerplate Code

To experiment with the transform properties, I’ll create a simple two-dimensional box using the <View> component. Inside App.js, add the following lines of code:


import React from 'react';
import { StyleSheet, View } from 'react-native';
export default function App() {
  return (
    <View style={styles.container}>
    {/* Box */}
      <View style={[styles.box,styles.transforms]}>
      </View>
    </View>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  box:{
    width:100,
    height:100,
    backgroundColor:'violet',
  },
  transforms:{
    transform:[]
  }
});

The above code should render a violet, square box at the center of your screen as shown:

Violet 2D box

The <View> component that renders the box on the screen also takes an array of styles. The first object represents some minimal styles to give background color, height, and width to the box. On the other hand, the second object represents the styles where you can assign the transform properties.

The transform property is a key that expects an array of objects. Each object is a transform property you wish to apply to the given element. There are primarily three transform properties—translate, scale, and rotate. Let’s first look at the translate property in action.

The Translate Property

The word translate literally means moving or displacing an item from one place to another. You can only move an object either horizontally or vertically. The below diagram represents a 2D coordinate axis with an object placed at the origin. Your element will be translated with respect to the below reference axis.

Translate axis

You can move your element along the x-axis by specifying the translateX property to your transform array as shown below:


transforms:{
    transform:[
      {translateX:50},
     ]
    }

The above code should shift your object 50 units to the right of its current position (i.e., origin):

Translate on x-axis

Similarly, if you translate the object along the y-axis, you can specify the translateY property as shown:


transforms:{
    transform:[
      {translateY:50},
     ]
    }

A positive value to translateY will cause your element to shift vertically downward, and a negative value will cause your element to shift vertically upward. In this case, your object will move down vertically by 50 units.

Translate on y-axis

It’s important to note that the y-axis for the translate property is reversed with respect to the conventional y-axis in geometry.

Finally, you can also translate your object along both axes simultaneously. You can pass an array to the translate property as shown:


transforms:{
    transform:[
      {translat:50,50},
     ]
    }

The above code would displace your object 50 units along both the x-axis and the y-axis. Your object’s final position would look like this:

Translate on both axes

Next, let’s explore the rotate property with some examples.

The Rotate Property

The rotate property allows you to orient your object along different axes of rotation. You can rotate it along the x-axis, the y-axis, or the z-axis. A rotational plane is a three-dimensional plane as shown below:

Coordinate axes of rotation

The above diagram represents a 3D rotational plane. The x- and y-axes are oriented toward the plane, and the z-axis is oriented toward you.

To rotate an object along the x-axis, you can specify the rotateX property. This property expects a string value for the angle of rotation, along with the unit of the specified angle. It can be either rad for radians or deg for degrees.


transforms:{
    transform:[
    {rotateX:"60deg"},
     ]
    }

The above code will cause your object to rotate inward with respect to your screen.

Rotation along x-axis

Thus, your violet box, which originally looked like a two-dimensional square, would now appear to be a trapezium.

Rotation along x-axis

In a similar fashion, you can specify the rotation along the y-axis using the rotateY property as shown:

Rotation along y-axis

transforms:{
    transform:[
    {rotateY:"60deg"},
     ]
    }

The above code would change the orientation of your element like this:

The rotateZ property will rotate your object along the z-axis of rotation. Let’s give the rotateZ property a value of 60deg and see what it looks like.


transforms:{
    transform:[
    {rotateZ:"60deg"},
     ]
    }

Your rotated object will look like this:

Rotation along z-axis

If you’ve used image editing tools that allow you to flip or rotate an image, they’re rotating the image along the z-axis of rotation. Let’s look at the last property in action, the scale property.

The Scale Property

The scale property specifies how large the object should be with respect to its original size. Originally, all elements are given a scale of 1, meaning their size is what you specify using the height and width style properties.

You can scale the object to become twice its size by giving it a scale of 2 as shown below:


transforms:{
    transform:[
         {scale:2},
     ]
    }

The above would cause your object to grow twice in size:

Scaled 2x in both directions

Alternatively, you can scale along the x-axis or the y-axis, as well. In this context, scaling twice along the x-axis would mean elongating the object’s width.


transforms:{
    transform:[
         {scaleX:1},
     ]
    }

Therefore, the object, which was originally a square, would now appear as a rectangle:

Scaled 2x along x-axis

Similarly, if you scale the object along the y-axis, it would elongate by its length instead.

Scaled 2x along y-axis

Use Transforms in a Product Page

Now that you understand how transforms work, let’s use the knowledge in a practical project. As I mentioned at the beginning, transforms are used extensively in animations. Another area where they’re widely used is in e-commerce applications or educational apps.

A lot of e-commerce applications allow you to view a product from different angles and zoom in on the product image. We can use the rotateZ and scale properties to implement something similar for our own app. Let’s create a simple product page that displays the image of a product and allows you to zoom in, zoom out, and rotate the product’s image.

Create Product Page UI

Let’s create a simple UI that renders some <View> components with some buttons. Inside App.js, add the following code:


import React, { useState } from 'react';
import { View, StyleSheet,TouchableOpacity, Text, Image } from 'react-native';
export default function App() {
  const productUri="https://ik.imagekit.io/pibjyepn7p9/71fwbMm1NBL._AC_SL1500__LAYt84BAJ.jpg"
    const [productStyles,setProductStyles]=useState()
  const zoomIn=()=>{
  }
  const zoomOut=()=>{
  }
  const rotate=()=>{
  }
  return (
    <>
    <View style={styles.header}>
      <Text style={styles.headerText}>Exploring Transforms</Text>
    </View>
    <View style={styles.container}>
          <View style={styles.productContainer}>
            <Image source={{uri:productUri}} style={[styles.productImage,productStyles]} />
            <View style={styles.productActionButtonsContainer}>
              <TouchableOpacity onPress={zoomIn} style={styles.productActionButton}>
                <Text style={{ color:'white'}}>Zoom In</Text>
              </TouchableOpacity>
              <TouchableOpacity onPress={zoomOut} style={styles.productActionButton}>
                <Text style={{ color:'white'}}>Zoom Out</Text>
              </TouchableOpacity>
              <TouchableOpacity onPress={rotate} style={styles.productActionButton}>
                <Text style={{ color:'white'}}>Rotate</Text>
              </TouchableOpacity>
            </View>
          </View>
    </View>
    </>
  );
}
const styles = StyleSheet.create({
  header:{
    backgroundColor:'#6495ED',
    display:'flex',
    flexDirection:'row',
    justifyContent:'center',
    height:150,
    marginTop:-50
  },
  headerText:{
    fontWeight:'bold',
    fontSize:20,
    marginTop:100,
    color:'white'
  },
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor:'#fefefe'
  },
  productContainer:{
    height:300,
    display:'flex',
    flexDirection:'column',
    alignItems:'center',
    justifyContent:'space-between'
  },
  productImage:{
    height:120,
    width:100
  },
  productActionButton:{
    backgroundColor:'#6495ED',
    padding:10,
    margin:5,
    borderRadius:6
  },
  productActionButtonsContainer:{
    display:'flex',
    flexDirection:'row',
    justifyContent:'space-between',
    marginTop:20
  }
});

The above code renders a header, an image of the product, and some buttons at the bottom. If you’ve done everything correctly, you should be able to see a minimal UI.

Product demo page UI

Great! Next, let’s create the functionalities for the buttons to zoom in, zoom out, and rotate the image.

Add Transforms to Product Image

First, we need to create a state that contains some default transform styles for our image.


const [productStyles,setProductStyles]=useState({
    transform:[
      {scale:1},
      {rotateZ:'0deg'}
    ]
  })

Since initially, the product is of the original size and unrotated, we have kept the scale property to be 1 and rotateZ to be ‘0deg’. When the user presses the Zoom In button, we need to set the scale property to 2. Let’s do that inside the zoomIn method.


const zoomIn=()=>{
    setProductStyles({
      transform:[
        {scale:2},
        {rotateZ:productStyles.transform[1].rotateZ}
      ]
    })
  }

Similarly, we can populate the zoomOut method to set the scale back to 1.


const zoomOut=()=>{
    setProductStyles({
      transform:[
        {scale:1},
        {rotateZ:productStyles.transform[1].rotateZ}
      ]
    })
  }

Lastly, we need to make the rotate method functional. Inside the rotate method, we first extract the current degree of rotation that the product image has using a regex pattern. Consequently, we add another 90 to this number to compute our new degree of rotation. Thus, if originally the product image was at zero degrees, it should now be rotated to 90 degrees.


const rotate=()=>{
    const currentDegreeOfRotation=productStyles.transform[1].rotateZ.match(/\d+/)[0];
    const newDegreeOfRotation=parseInt(currentDegreeOfRotation)+90;
  }

Then, we assign the newDegreeOfRotation to the rotateZ property inside the transform object of our productStyles state.


setProductStyles({
      transform:[
        {scale:productStyles.transform[0].scale},
        {rotateZ:newDegreeOfRotation+"deg"}
      ]
    })

And that’s it! Our zoom in, zoom out, and rotate buttons should now be functional. Let’s test our product demo page on a physical device.

Test Product Demo Page

Expo allows you to test your apps live on a physical device as you build them. If you’re running your project on an emulator, here’s a simple guide where you can run your app on your own device using Expo Go. On the other hand, if you’re already running it live on your device, then we’re all good to test.

If your product page works as shown above, it’s in good shape! If something seems off, you can refer to the entire App.js code for the product page below


import React, { useState } from 'react';
import { View, StyleSheet,TouchableOpacity, Text, Image } from 'react-native';
export default function App() {
  const productUri="https://ik.imagekit.io/pibjyepn7p9/71fwbMm1NBL._AC_SL1500__LAYt84BAJ.jpg"
  const [productStyles,setProductStyles]=useState({
    transform:[
      {scale:1},
      {rotateZ:'0deg'}
    ]
  })
  const zoomIn=()=>{
    setProductStyles({
      transform:[
        {scale:2},
        {rotateZ:productStyles.transform[1].rotateZ}
      ]
    })
  }
  const zoomOut=()=>{
    setProductStyles({
      transform:[
        {scale:1},
        {rotateZ:productStyles.transform[1].rotateZ}
      ]
    })
  }
  const rotate=()=>{
    const currentDegreeOfRotation=productStyles.transform[1].rotateZ.match(/\d+/)[0];
    const newDegreeOfRotation=parseInt(currentDegreeOfRotation)+90;
    setProductStyles({
      transform:[
        {scale:productStyles.transform[0].scale},
        {rotateZ:newDegreeOfRotation+"deg"}
      ]
    })
  }
  return (
    <>
    <View style={styles.header}>
      <Text style={styles.headerText}>Exploring Transforms</Text>
    </View>
    <View style={styles.container}>
          <View style={styles.productContainer}>
            <Image source={{uri:productUri}} style={[styles.productImage,productStyles]} />
            <View style={styles.productActionButtonsContainer}>
              <TouchableOpacity onPress={zoomIn} style={styles.productActionButton}>
                <Text style={{ color:'white'}}>Zoom In</Text>
              </TouchableOpacity>
              <TouchableOpacity onPress={zoomOut} style={styles.productActionButton}>
                <Text style={{ color:'white'}}>Zoom Out</Text>
              </TouchableOpacity>
              <TouchableOpacity onPress={rotate} style={styles.productActionButton}>
                <Text style={{ color:'white'}}>Rotate</Text>
              </TouchableOpacity>
            </View>
          </View>
    </View>
    </>
  );
}
const styles = StyleSheet.create({
  header:{
    backgroundColor:'#6495ED',
    display:'flex',
    flexDirection:'row',
    justifyContent:'center',
    height:150,
    marginTop:-50
  },
  headerText:{
    fontWeight:'bold',
    fontSize:20,
    marginTop:100,
    color:'white'
  },
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor:'#fefefe'
  },
  productContainer:{
    height:300,
    display:'flex',
    flexDirection:'column',
    alignItems:'center',
    justifyContent:'space-between'
  },
  productImage:{
    height:120,
    width:100
  },
  productActionButton:{
    backgroundColor:'#6495ED',
    padding:10,
    margin:5,
    borderRadius:6
  },
  productActionButtonsContainer:{
    display:'flex',
    flexDirection:'row',
    justifyContent:'space-between',
    marginTop:20
  }
});

While this allows you to test your app directly on a physical device, you might also want to test your app on multiple devices at the same time. You can check out Waldo, a no-code testing platform that allows you to directly record and run tests in your browsers on a number of devices concurrently.

Explore Further

I hope you’re all comfy with using transforms in your React Native projects. It’s a tricky thing to understand at first, but it will ease a lot of pain when you’re developing animations or building custom components to rotate and scale images, SVGs, etc.

The syntax itself is a bit difficult to understand, so make sure you read through the docs to stay updated on the latest syntax and API changes. If you wish to dive deeper, go ahead and try animating a car from one end to the other end of the screen. It’ll be an interesting exercise, and if you get stuck, here’s a spoiler—use the translateX property. Until next time!

Automated E2E tests for your mobile app

Waldo provides the best-in-class runtime for all your mobile testing needs.
Get true E2E testing in minutes, not months.

Reproduce, capture, and share bugs fast!

Waldo Sessions helps mobile teams reproduce bugs, while compiling detailed bug reports in real time.