Animation

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.

CSS transitions

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> ); }

CSS Keyframe animations

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 (
<Block
extend={{
animationName: {
'0%': { opacity: 0 },
'100%': { opacity: 1 },
},
animationDuration: '1s',
animationIterationCount: 'infinite',
}}
>
Hello Keyframes
</Block>
);
};

Animating change in state

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}>
<Button
onClick={() => {
setItems([...items, items.length]);
}}
>
Add
</Button>
<Button
onClick={() => {
setItems(items.slice(0, -1));
}}
intent="destructive"
>
Remove
</Button>
</View>
<Spacer />
<TransitionGroup component={null}>
{items.map((item) => {
return (
<Transition
key={item}
timeout={750}
onEnter={(node) => node.scrollTop}
>
{(state) => {
return (
<>
<Spacer />
<Block
extend={{
display: 'flex',
...getTransitionStyle(state),
}}
>
<Card key={item}>
<View
padding={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.

CSS Reflow hack

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

2024 © Volvo Car Corporation