To apply animation to your application you can use three different strategies based on your needs and requirements.
Disclaimer: There are currently no official UX/design guidelines on how to apply animation or motion in our web applications. Once there are, we should be able to expose things like duration and easing in our theme configurations.
The easiest way to apply animation is by simply using the CSS transition property.
<Block
extend={{
transition: '1s opacity ease-out',
}}
>
Hello
</Block>
Let's explore a more elaborate example with a button that toggles the opacity of a component:
import React, { useState, Component } from "react";
import { Button, View, Text } from "vcc-ui";
export default function App() {
const [reveal, toggleReveal] = useState(false);
return (
<View spacing={4}>
<Button
onClick={() => {
toggleReveal(!reveal);
}}
>
Show {reveal ? "Less" : "More"}
</Button>
<Text
variant="hillary"
extend={{ transition: "1s ease-in", opacity: reveal ? 1 : 0 }}
>
And you shall be revealed!
</Text>
</View>
);
}
For more complex animation requirements you can utilise CSS @keyframes. In VCC UI this works by passing a keyframe object to the animation-name
property. You can use all other animation-
props like would normally do.
import { Block } from 'vcc-ui';const AnimatedBlock = () => {return (<Blockextend={{animationName: {'0%': { opacity: 0 },'100%': { opacity: 1 },},animationDuration: '1s',animationIterationCount: 'infinite',}}>Hello Keyframes</Block>);};
Sometimes you want animate adding and removing to a list of items in your application state. This can be tricky because React will typically render the DOM before you get the chance to apply any transitions. To solve this you can use the react-transition-group
package.
The Transition component lets you describe a transition from one component state to another over time with a simple declarative API. Most commonly it's used to animate the mounting and unmounting of a component, but can also be used to describe in-place transition states as well.
Inside the Transition component we can access a render prop that gives us the current state of the transition: entered, entering, exiting and exited. We can create a function that returns a style object describing what an item should render like in a given state.
function getTransitionStyle(state) {if (state === 'entered') {return {transform: 'scale(1)',opacity: 1,};}if (state === 'entering') {return {opacity: 1,transform: 'scale(1)',transition: 'all 750ms cubic-bezier(0.175, 0.885, 0.32, 1.275)',};}if (state === 'exiting') {return {opacity: 0,transform: 'scale(0)',transition: 'all 750ms',};}if (state === 'exited') {return {transform: 'scale(0)',opacity: '0',};}}
This will create a bouncy zoom in effect on "entering", and a zoom out effect on "exiting".
Here's a simple application using the getTransitionStyle
function together with the <TransitionGroup>
and <Transition>
components:
export default function App() {const [items, setItems] = useState([0]);return (<View direction="row" padding={2}><View spacing={1}><ButtononClick={() => {setItems([...items, items.length]);}}>Add</Button><ButtononClick={() => {setItems(items.slice(0, -1));}}intent="destructive">Remove</Button></View><Spacer /><TransitionGroup component={null}>{items.map((item) => {return (<Transitionkey={item}timeout={750}onEnter={(node) => node.scrollTop}>{(state) => {return (<><Spacer /><Blockextend={{display: 'flex',...getTransitionStyle(state),}}><Card key={item}><Viewpadding={2}extend={{ flexGrow: '1' }}justifyContent="center"><Text subStyle="emphasis">Item {item}</Text></View></Card></Block></>);}}</Transition>);})}</TransitionGroup></View>);}
The above example simply renders a list of cards from an array with numbers. The add button adds a new item to the end of the array. The remove button removes the last item from the array.
A key thing to notice here is the onEnter
function which calls the node.scrollTop
property. We have to do this to force a repaint/reflow so that the browser applies the exited transition state before applying the entering state. This is a known limiation and hack when using react-transition-group and CSS-in-JS libraries