mirror of
https://github.com/hanzoai/gui.git
synced 2026-10-25 02:31:41 +00:00
316 lines
8.2 KiB
Plaintext
316 lines
8.2 KiB
Plaintext
---
|
|
title: Animations
|
|
description: Swap out animation drivers per-platform or at runtime
|
|
---
|
|
|
|
<HeroContainer>
|
|
<AnimationsDemo />
|
|
</HeroContainer>
|
|
|
|
```tsx hero template=Animations
|
|
|
|
```
|
|
|
|
<Highlights
|
|
disableLinks
|
|
features={[
|
|
`Animate any style prop with animation config per-prop.`,
|
|
`Can animate across all states (media queries, hover, etc).`,
|
|
`Multiple drivers you can swap out with type safety.`,
|
|
`SSR safe mount animations.`,
|
|
`Enter and exit animations with AnimatePresence.`,
|
|
]}
|
|
/>
|
|
|
|
Add animations to Gui with an animation driver. Animation drivers are designed to be swappable, so you can use lightweight CSS animations or other web-focused animation libraries on the web, while using larger but more advanced libraries like Reanimated on native - all without having to change a line outside of configuration.
|
|
|
|
For this guide, we'll use the React Native driver as an example, but you can choose from several [Animation Drivers](/docs/core/animation-drivers). For mount and unmount animations, see [AnimatePresence](/docs/core/animate-presence).
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
yarn add @hanzogui/animations-react-native
|
|
```
|
|
|
|
Then add it to your [Gui config](/docs/core/configuration):
|
|
|
|
```tsx
|
|
import { createAnimations } from '@hanzogui/animations-react-native'
|
|
import { createGui } from '@hanzo/gui'
|
|
|
|
export default createGui({
|
|
animations: createAnimations({
|
|
bouncy: {
|
|
damping: 10,
|
|
mass: 0.9,
|
|
stiffness: 100,
|
|
},
|
|
lazy: {
|
|
damping: 18,
|
|
stiffness: 50,
|
|
},
|
|
quick: {
|
|
damping: 20,
|
|
mass: 1.2,
|
|
stiffness: 250,
|
|
},
|
|
}),
|
|
// ...
|
|
})
|
|
```
|
|
|
|
## Usage
|
|
|
|
The `transition` prop accepts the name of an animation you've configured. By default, animations will
|
|
apply to all animatable styles, similar to setting `all` in a CSS `transition`.
|
|
|
|
Here's an example animating `hoverStyle`:
|
|
|
|
<HeroContainer>
|
|
<AnimationsHoverDemo />
|
|
</HeroContainer>
|
|
|
|
```tsx hero template=AnimationsHover
|
|
|
|
```
|
|
|
|
### The transition prop rules
|
|
|
|
If you add a `transition` prop, you must always keep the prop. If you need the animation to be disabled,
|
|
pass false, null or even undefined if it suits you.
|
|
|
|
The spring-based animation drivers have expensive hooks that would degrade runtime
|
|
performance if present on every component. As a workaround, the animation hooks
|
|
are called conditionally based on whether the `transition` key is present in the props object.
|
|
|
|
So, `<Square transition={isActive ? 'bouncy' : null} />` rather than
|
|
`<Square {...isActive && { transition: 'bouncy' }} />`.
|
|
|
|
If you'd like to remove or add a transition prop after a component has already rendered, you'd have
|
|
to change the key.
|
|
|
|
### enterStyle
|
|
|
|
Setting `enterStyle` on a component tells it to start with those styles,
|
|
then animate to the base styles after mount:
|
|
|
|
<HeroContainer>
|
|
<AnimationsEnterDemo />
|
|
</HeroContainer>
|
|
|
|
```tsx hero template=AnimationsEnter
|
|
|
|
```
|
|
|
|
### Granular animations
|
|
|
|
The `transition` prop accepts a string or a more complex object to customize
|
|
animations per-property.
|
|
|
|
You can specify animations for specific properties using an object:
|
|
|
|
```tsx
|
|
import { YStack } from '@hanzo/gui'
|
|
|
|
export default () => (
|
|
<YStack
|
|
transition={{
|
|
// only x and y will apply animations
|
|
x: 'bouncy',
|
|
y: {
|
|
type: 'bouncy',
|
|
overshootClamping: true,
|
|
},
|
|
}}
|
|
/>
|
|
)
|
|
```
|
|
|
|
Note that values can either map to `AnimationKey` as a string, or to
|
|
`{ type: AnimationKey, ...configuration }`
|
|
|
|
You can set a default animation using a two-element array with the default in the
|
|
first position:
|
|
|
|
```tsx
|
|
import { YStack } from '@hanzo/gui'
|
|
|
|
export default () => (
|
|
<YStack
|
|
transition={[
|
|
// all attributes get "bouncy"
|
|
'bouncy',
|
|
// these are customized
|
|
{
|
|
y: 'slow',
|
|
scale: {
|
|
type: 'fast',
|
|
repeat: 2,
|
|
},
|
|
},
|
|
]}
|
|
/>
|
|
)
|
|
```
|
|
|
|
### Delay
|
|
|
|
You can add a delay before animations start using the array syntax with a `delay` property (in milliseconds):
|
|
|
|
```tsx
|
|
import { Square, XStack } from '@hanzo/gui'
|
|
|
|
export default () => (
|
|
<XStack gap="$2">
|
|
{[0, 1, 2, 3].map((i) => (
|
|
<Square
|
|
key={i}
|
|
transition={['bouncy', { delay: i * 100 }]}
|
|
enterStyle={{ opacity: 0, scale: 0.5, y: 20 }}
|
|
size={50}
|
|
bg="$color10"
|
|
/>
|
|
))}
|
|
</XStack>
|
|
)
|
|
```
|
|
|
|
This creates a staggered animation effect where each square animates 100ms after the previous one. The delay works with all animation drivers and applies to enter, exit, and style change animations.
|
|
|
|
### Enter/Exit Transitions
|
|
|
|
You can specify different animations for enter (mount) and exit (unmount) transitions. This is useful when you want elements to enter slowly but exit quickly, or vice versa:
|
|
|
|
```tsx
|
|
import { AnimatePresence, View } from '@hanzo/gui'
|
|
|
|
export default ({ show }) => (
|
|
<AnimatePresence>
|
|
{show && (
|
|
<View
|
|
key="panel"
|
|
transition={{ enter: 'lazy', exit: 'quick' }}
|
|
enterStyle={{ opacity: 0, y: 20 }}
|
|
exitStyle={{ opacity: 0, y: -20 }}
|
|
/>
|
|
)}
|
|
</AnimatePresence>
|
|
)
|
|
```
|
|
|
|
The `enter` and `exit` keys accept any animation name from your config. You can also combine them with a `default` for property changes that happen while the element is mounted:
|
|
|
|
```tsx
|
|
// enter slowly, exit quickly, property changes use medium speed
|
|
<View
|
|
transition={{ enter: 'lazy', exit: 'quick', default: 'bouncy' }}
|
|
enterStyle={{ opacity: 0 }}
|
|
exitStyle={{ opacity: 0 }}
|
|
/>
|
|
```
|
|
|
|
Or use enter/exit with the array syntax to include delay and per-property animations:
|
|
|
|
```tsx
|
|
// enter with lazy, exit with quick, delay 200ms, x uses its own animation
|
|
<View
|
|
transition={['bouncy', { enter: 'lazy', exit: 'quick', delay: 200, x: 'slow' }]}
|
|
enterStyle={{ opacity: 0, x: -100 }}
|
|
exitStyle={{ opacity: 0, x: 100 }}
|
|
/>
|
|
```
|
|
|
|
This works with all four animation drivers (CSS, React Native, Reanimated, Motion).
|
|
|
|
### Pseudo-Style Transitions
|
|
|
|
You can specify different transitions for entering and exiting pseudo states like hover, press, and focus. This follows CSS-like semantics where entering a state uses that state's transition, and exiting uses the base transition:
|
|
|
|
```tsx
|
|
import { Square } from '@hanzo/gui'
|
|
|
|
export default () => (
|
|
<Square
|
|
size={100}
|
|
bg="$color5"
|
|
transition="1000ms" // slow exit (returning to base)
|
|
hoverStyle={{
|
|
transition: '200ms', // fast enter (going to hover)
|
|
bg: '$color10',
|
|
}}
|
|
/>
|
|
)
|
|
```
|
|
|
|
In this example:
|
|
|
|
- **Hover enter**: Uses the 200ms transition (snaps quickly to hover state)
|
|
- **Hover exit**: Uses the 1000ms base transition (fades slowly back)
|
|
|
|
This works with all pseudo styles:
|
|
|
|
```tsx
|
|
<View
|
|
transition="slow"
|
|
hoverStyle={{
|
|
transition: 'quick',
|
|
scale: 1.1,
|
|
}}
|
|
pressStyle={{
|
|
transition: 'bouncy',
|
|
scale: 0.95,
|
|
}}
|
|
focusStyle={{
|
|
transition: 'quick',
|
|
borderColor: '$blue10',
|
|
}}
|
|
/>
|
|
```
|
|
|
|
And with group pseudo states:
|
|
|
|
```tsx
|
|
<View group="card">
|
|
<Square
|
|
size={50}
|
|
bg="$color5"
|
|
transition="1000ms"
|
|
$group-card-hover={{
|
|
transition: '200ms',
|
|
bg: '$color10',
|
|
}}
|
|
/>
|
|
</View>
|
|
```
|
|
|
|
Priority follows CSS specificity intuition: press > hover > focus. This works with all animation drivers.
|
|
|
|
### animateOnly
|
|
|
|
The `animateOnly` prop will limit your animation config to certain keys. It
|
|
accepts an array of strings that correspond to style property names.
|
|
|
|
## What to know when animating
|
|
|
|
### Conditional animations and HMR
|
|
|
|
The animation hooks are heavy, which initially meant we either had to choose
|
|
great performance or animations. We settled on a trade-off: we track if the
|
|
`transition` prop is set, and if so, we enable the hook. If it is ever set, even
|
|
just once, then the hooks will continue to run for the remainder of the
|
|
component lifecycle. This means if you ever plan to animate a component you
|
|
should keep `transition` always set on the component props. You can disable it
|
|
like so:
|
|
|
|
```tsx
|
|
<View transition={condition ? 'animation-name' : null} />
|
|
```
|
|
|
|
Note that because of this constraint, you may see an error if you add the
|
|
`transition` prop to a component in dev mode during HMR. Often just saving once more will remove the error, or reload the page if needed.
|
|
|
|
## See Also
|
|
|
|
- [AnimatePresence](/docs/core/animate-presence) - Mount and unmount animations
|
|
- [Animation Drivers](/docs/core/animation-drivers) - Choose and configure animation drivers
|