Our intentions are to build something like shown below. A very basic version as of now, but hopefully keep on adding more effects.
Credits for css: https://daneden.github.io/animate.css/
NOTE: This is a WIP article as effects will be added incrementally.
The working code can be found here
We will be using the CSS3 animation property for animating our elements. We will build a small <Effect> component and a helper function for animating thing dynamically.
Animation properties
The animation
is a shorthand property for several others:
- name: the animation’s name
- duration: how long the transition lasts
- timing-function: how the intermediate states are calculated
- delay: to start the animation after a certain amount of time
- iteration-count: how many times the animation should be performed
- direction: if the animation should be reversed or not
- fill-mode: what styles are applied before the animation starts and after it ends
NOTE: We will be using the vendor-neutral syntax for animation properties.
The Building Blocks of Animations using CSS
CSS animations are made up of two basic building blocks as outlined below.
- Keyframes — define the stages and styles of the animation.
- Animation Properties — assign the @keyframes to a specific CSS element and define how it is animated.
Let’s look at each individually.
Keyframes
Keyframes define what the animation looks like at each stage of the animation timeline. Each @keyframes consists of the following properties.
- Name of the animation
A name that describes the animation, for example, “rotate” - Stages of the animation
Each stage of the animation is represented as a percentage. For e.g.
— 0% represents the start of the animation
— 100% represents the ending state of the animation.
Alternatively, two named stages are available, i.e. from and to. ‘from’ is the start state and ‘to’ is the end state.
- from — 0% represents the start of the animation
- to — 100% represents the end of the animation.
NOTE: Multiple intermediate states can be added in between.
Let’s see how to create a simple @keyframes to create the “rotate” effect. We name our keyframe as “rotateOut” and give it a start and end setting. When the animation starts, we set the origin to the center and the opacity to 1. And at the end of the animation, we use the rotate3d function with the transform property.
The rotate3d() function rotates the element along the x, y and the z-axes using the angle provided as an argument.
You can provide the axis/axes of rotation using a <number> for the first three arguments.
The fourth argument specified the angle. You can provide the angle as any valid <angle> value (i.e. in degrees, gradians, radians, or turns).
The example below rotates 200 degrees around the z-axis. We also create a .rotateOut CSS class which can be applied to any element. Do note that the animation-name is set to rotateOut. This connects the CSS class to the animation rotateOut that we just created.
The below animation runs infinitely, and for 2s in each cycle.
@keyframes rotateOut {
from {
transform-origin: center;
opacity: 1;
}
to {
transform-origin: center;
transform: rotate3d(0, 0, 1, 200deg);
opacity: 0;
}
}
.rotateOut {
animation: rotateOut 2s infinite;
}
NOTE: transform-origin property allows you to change the position of the transformed elements. 2D transformations can change the x- and y-axis of an element. 3D transformations can also change the depth, i.e. the z-axis of an element.
Check the working code on how to infinitely rotate an element in 3D space.
The CSS for the demo in the HTML is given below.
.container {
position: relative;
height:100vh;
display: flex;
justify-content: center;
align-items: center;
}
.sprite {
background-color: pink;
font-size: 8em;
width:60px;
height:60px;
}
HTML
<div class="container">
<div class="sprite rotateOut"></div>
</div>
Now, let’s use this knowledge and let’s see how we can apply this learning to react.
React Animation
I will be using the Hooks version for simplicity. Also, I am using the codepen for now. But as the library grows bigger I will move to a dedicated github repository.
First, let’s create a functional component called Effect which will add animation effects to the child components.
The <Effect/> Component
function Effect(props) {
const childs =
React.Children.map(props.children, child => {
return React.cloneElement(child, {
className: child.props.className +
' animated ' + props.name,
onAnimationEnd: (e) => {
e.target.classList.remove("animated", props.name);
}
});
});
return (
childs
);
}
The Effect component is like a HOC component (but not exactly). Let’s understand the code line by line.
- Loop through all the child component
- For every child component clone the element and pass in the custom CSS class name property, ‘animated ‘ and whatever is passed in the props.
- Add an onAnimationEnd event handler. This event fires when the animation ends. Here we do a cleanup activity and removes the classes that we added.
- And return the new enhanced child component.
Let’s see how we can use the Effect component to add animation effects. First, let us define some CSS animation classes and @keyframes.
I am taking a couple of examples. The one given gives the ‘spin’ effect to the wrapped component.
@keyframes spin {
from {transform:rotate(0deg);}
to {transform:rotate(360deg);}
}.rotate {
color: blue;
animation-name: spin;
}.animated {
animation-duration: 1s;
animation-fill-mode: both;
}
I create a @keyframes named spin, which will transform the element by rotating it 360 degrees.
Also, create a .rotate CSS class, that invokes this animation. We also create a default class called .animated with the duration and fill mode.
This .animated class is automatically added in the Effect component that we just created.
Let’s see how we can use the Effect Component.
function App () {
return (
<div className="container">
<div className="flex-item">
<Effect name="rotate">
<span className="icon">❤️</span>
</Effect>
</div>
</div>
)
}
NOTE: The name that you pass to the Effect component is the name of the CSS class.
Now, run your application and you will see that the little ❤️ will rotate when the component is loaded.
To extend the animation library, keep on creating classes and keyframes for different effects like swing, swirl, flip, zoom in, etc.
Let us now add one more feature wherein the animation can be invoked at runtime, say, onClick.
For this, we will create a custom hook, useAnim. Let’s take a look at the custom hook first and then an application of it.
useAnim Custom Hook
function useAnim () {
const anim = (animName, e) =>{
e.target.classList.remove("animated");
e.target.classList.add("animated",animName);
e.target.addEventListener('animationend', (e) => {
removeAnim(e, animName);
});
}
const removeAnim = (e, animName) => {
e.target.classList.remove("animated", animName);
}
anim.run = (animName) => {
return anim.bind(null, animName);
}
return anim;
}
Let’s understand the code line by line.
- We create an inline anim function that takes an animName (which is the class name) as the parameter.
- In the anim function we clean up the old animation if any by removing the “animated” class, we add the “animated” class and the new animName passed by calling component.
- We then hook into the animationend event for cleanup activity.
- We also add a “run” method on the anim function which returns a new function bound with the animName that the calling component passes.
Let us create one more CSS effect called “slide-in-left” where an element will appear to move smoothly from left to right.
@keyframes slideInLeft {
from {
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
visibility: visible;
}to {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}.slide-in-left {
animation-name: slideInLeft;
}
Let’s take a look at the usage of the custom hook.
function App () {
const anim = useAnim();
return (
<div className="container">
<div className="flex-item">
<Effect name="rotate">
<span className="icon">❤️</span>
</Effect>
</div> <div className="flex-item">
<span className="icon"
onClick={anim.run("slide-in-left")}>🚓</span>
<h4>click to move</h4>
</div>
</div>
)
}
Now to execute the animation on click, we just have, invoke the useAnim() and the anim.run() method with the class name of the animation as the parameter.
The 🚓 will move from left to right when the element is clicked. Now, if you want the car to run on load as well, you can wrap it within the <Effect/> component as shown below.
<div className="flex-item">
<Effect name="slide-in-left">
<span className="icon"
onClick={anim.run("slide-in-left")}>🚓</span>
</Effect>
<h4>click to move</h4>
</div>
Hope you liked this.
NOTE: I have kept the code simple for learning purpose. Performance and other optimizations not yet considered but will address as and when needed.