Animate everything in ReactNative with a single line of code!
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 playLayoutAnimation.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 asscaleX
, 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. Accepttype
andproperty
.update
— directive, how element should behave if it styles has changed. Accept onlyproperty
.
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
orthunks
, 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!