Animate everything in ReactNative with a single line of code!

Andrii Drozdov
5 min readApr 19, 2019

--

Prologue.

I know that sometimes animation in ReactNative can be tricky, but now I will show a secret cheat, that will help you to animate nearly anything!

This article will be just a number of examples, but this feature will help you to animate nearly ANYTHING!

Episode 1. The beginning.

Start a new project :)

Use react-native init OneLineAnimation to generate a project from scratch.

Create helpers folder under src folder with index.js and animation.js files, just for better decomposition, and a component file.

Our file structure:

| - /OneLineAnimation
| - - /src
| - - - /helpers
| - - - - /index.js
| - - - - /animation.js

Episode 2. Magic.

Let's implement our /helpers/animation.js file:

import { LayoutAnimation } from 'react-native'
import { UIManager } from "react-native";

const CONFIG = {
duration: 300,
create: {
type: LayoutAnimation.Types.linear,
property: LayoutAnimation.Properties.opacity
},
update: {
type: LayoutAnimation.Types.easeInEaseOut
}
}

export function animate () {
LayoutAnimation.configureNext(CONFIG)
}

And update/helpers/index.js file:

export { animate } from './animation'

Now all your animation just rely on you mark-up.

Lets walk through the code.

  • duration — how long animation will play
  • LayoutAnimation.Types — has 6 options:
    1. spring
    2. linear
    3. easeInEaseOut
    4. easeIn
    5. easeOut
    6. keyboard

Play with this values, take a look on how it works, I’m sure that you will find animation what will fit you needs.

  • LayoutAnimation.Properties — has 4 options:
    1. opacity — element will appear with 0 opacity and it will scale to 1.
    2. scaleX — element will appear and will be increasing horizontaly.
    3. scaleY — same as scaleX, only size will be increasing verticaly.
    4. scaleXY — all dimensions will be used in scalling.
  • create — number of directives, how element should behave when it does not exist, and it appear on re-render. Accept type and property.
  • update — directive, how element should behave if it styles has changed. Accept only property.

The config that we done will mostly fill all of your needs.

How this should be used?

Pretty simple, each time when you want to animate something you need to call it before function what will call re-render and change layout. For example:

  • in your onPress handlers
  • in your render() (but this option is NOT recommended, since you will force native code to listen on changes on each re-render)
  • sagas or thunks, or any other place, what can affect you layout

Episode 3. Usage examples.

  • Collapse/Expand:

The regular case when you need animation is when you have expandable parts of the screen. For example:

You are never going to believe it, but all this animation is done with a single line!

Here’s a code with Hooks API:

import React, { useState, useCallback } from 'react'
import { View, Text } from 'react-native'

import { animate } from '../../helpers'

import Button from '../common/Button'

import styles from './styles'

export default function () {
const [isVisible, setIsVisible] = useState(false)

const handlePress = useCallback(
() => {
animate()
setIsVisible(!isVisible)
},
[isVisible]
)

return (
<>
<View style={styles.buttonContainer}>
<Button text="Toggle appearance" onPress={handlePress} />
</View>

{isVisible && (
<View style={styles.container}>
{Array(3).fill().map((_, index) => (
<View key={index} style={styles.textContainer}>
<Text>
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
</Text>
</View>
))}
</View>
)}
</>
)
}

With class component component will look like:

export default class Appearance extends Component {
state = {
isVisible: false
}

handlePress = () => {
animate()
this.setState({ isVisible: !this.state.isVisible })
}
render () {
return (
<>
<View style={styles.buttonContainer}>
<Button text="Toggle appearance" onPress={this.handlePress} />
</View>

{this.state.isVisible && (
<View style={styles.container}>
{Array(3).fill().map((_, index) => (
<View key={index} style={styles.textContainer}>
<Text>
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
</Text>
</View>
))}
</View>
)}
</>
)
}
}
  • Appear/Disappear:

Yup, still just one line :)

Here is a code sample:

export default function () {
const [selectedItem, setSelectedItem] = useState(-1)

const handlePress = useCallback(
(index) => {
animate()
setSelectedItem(selectedItem === index ? -1 : index)
},
[selectedItem]
)

return (
<View style={styles.container}>
{Array(3).fill().map((_, index) => (
<View key={index}>
<TouchableOpacity style={styles.touchableZone} onPress={() => handlePress(index)}>
<Text>Lorem Ipsum {index}</Text>
</TouchableOpacity>

{index === selectedItem && (
<View style={styles.textContainer}>
<Text>
Lorem Ipsum is simply dummy text.
</Text>
</View>
)}
</View>
))}
</View>
)
}
  • Movement:

And here to — one line :)

Here’s a sample:

import React, { useState, useCallback } from 'react'
import { View } from 'react-native'

import { animate } from '../../helpers'
import Button from '../common/Button'

import styles from './styles'

function getRandomIntInclusive () {
const min = 1
const max = 300

return Math.floor(Math.random() * (max - min + 1)) + min
}

export default function () {
const [position, setPosition] = useState({ top: 50, left: 50 })

const handlePress = useCallback(
() => {
const top = getRandomIntInclusive()
const left = getRandomIntInclusive()

animate()
setPosition({ top, left })
},
[position.top, position.left]
)

return (
<>
<View style={styles.buttonContainer}>
<Button text="Toggle movement" onPress={handlePress} />
</View>

<View style={[styles.movableBlock, position]} />
</>
)
}

Notice: I do randomization of the block position on each press.

Episode 4. Oh, wait, Android?

Yes, no problems with Mr. Android. Just small actions are required. All you need to do — is add a small directive to your project, you may call in any place you want. So, let’s update our animation.js file:

export function enableAnimation() {
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true);
}

Don’t forget to update our /helpers/index.js file:

export { animate, enableAnimation } from './animation'

Now we need to call this function on application boot (or in components where you apply LayoutAnimation). For example in root component in componentDidMount, constructor, saga, thunk, or else. For example:

import React, { Component } from 'react'import { enableAnimation } from './helpers'
import YouAwesomeComponent from './YouAwesomeComponent'
class App extends Component {
constructor(props) {
super(props)
enableAnimation()
}
render() {
return <YouAwesomeComponent />
}
}

Or via React hooks:

import React, { useEffect } from 'react'

import { enableAnimation } from './helpers'
import YouAwesomeComponent from './YouAwesomeComponent'

export default function () {
useEffect(
() => {
enableAnimation()
},
[false]
)

return <YouAwesomeComponent />
}

Epilogue.

As you see, this is a really great tool to provide animation in your project and save a LOT of time.

The only downside of this solution is uncontrollable animation, for example, if the handler where you have animation() function call and your handler changes the layout on a different part of the screen — everything will be animated, and this can call be a little bit unpredictable, but benefits of this usage are too great to ignore this powerful tool.

Peace and love, my friends!

Follow me for more!

Resources.

--

--

Andrii Drozdov
Andrii Drozdov

Written by Andrii Drozdov

CTO & developer, to whom every breath, is an injection of bits into CPU of my life

No responses yet