skaya
Version:
CLI SDK for full-stack automation: scaffold frontend, backend & blockchain. Future-ready for Web3, integrations, server components & logging.
454 lines (320 loc) • 12.8 kB
Markdown
# React animation
Motion for React offers a number of ways to animate your UI. Scaling from extremely simple prop-based animations, to more complex orchestration.
## Basic animations
You'll perform almost all animations on [a](/docs/react-motion-component.md) `[<motion />](/docs/react-motion-component.md)` [component](/docs/react-motion-component.md). This is basically a DOM element with motion superpowers.
```
import { motion } from "motion/react"
```
For basic animations, you can update values on [the](/docs/react-motion-component#animate.md) `[animate](/docs/react-motion-component#animate.md)` [prop](/docs/react-motion-component#animate.md):
```
<motion.div animate={{ opacity: 1 }} />
```
When any value in its animate prop changes, the component will automatically animate to the new target.
## Animatable values
Motion can animate any CSS value, even those that can't be animated by browsers, like `mask-image`. It supports:
* Numbers: `0`, `100` etc.
* Strings containing numbers: `"0vh"`, `"10px"` etc.
* Colors: Hex, RGBA, HSLA.
* Complex strings containing multiple numbers and/or colors (like `box-shadow`).
* `display: "none"/"block"` and `visibility: "hidden"/"visible"`.
### Value type conversion
In general, values can only be animated between two of the same type (i.e `"0px"` to `"100px"`).
Colors can be freely animated between hex, RGBA and HSLA types.
Additionally, `x`, `y`, `width`, `height`, `top`, `left`, `right` and `bottom` can animate between different value types.
```
<motion.div
initial={{ x: "100%" }}
animate={{ x: "calc(100vw - 50%)" }}
/>
```
It's also possible to animate `width` and `height` in to/out of `"auto"`.
```
<motion.div
initial={{ height: 0 }}
animate={{ height: "auto" }}
/>
```
**Note:** If additionally animating `display` in to/out of `"none"`, replace this with `visibility` `"hidden"` as elements with `display: none` can't be measured.
### Transforms
Unlike CSS, Motion can animate every transform axis independently:
* Translate: `x`, `y`, `z`
* Scale: `scale`, `scaleX`, `scaleY`
* Rotate: `rotate`, `rotateX`, `rotateY`, `rotateZ`
* Skew: `skew`, `skewX`, `skewY`
* Perspective: `transformPerspective`
`motion` components have enhanced `style` props, allowing you to set individual transforms:
```
<motion.section style={{ x: -20 }} />
```
Animating transforms independently provides great flexibility, especially around gestures.
```
<motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} />
```
Independent transforms perform great, but Motion's hybrid engine also uniquely offers hardware acceleration by setting `transform` directly.
```
<motion.li
initial={{ transform: "translateX(-100px)" }}
animate={{ transform: "translateX(0px)" }}
transition={{ type: "spring" }}
/>
```
**SVG note:** For SVG components, `x` and `y` **attributes** can be set using `attrX` and `attrY`.
### Transform origin
`transform-origin` has three shortcut values that can be set and animated individually:
* `originX`
* `originY`
* `originZ`
If set as numbers, `originX` and `Y` default to a progress value between `0` and `1`. `originZ` defaults to pixels.
```
<motion.div style={{ originX: 0.5 }} />
```
### CSS variables
Motion for React can animate the value of CSS variables, and also use CSS variables as animation targets.
#### Animating CSS variables
Sometimes it's convenient to be able to animate a CSS variable to animate many children:
```
<motion.ul
initial={{ '--rotate': '0deg' }}
animate={{ '--rotate': '360deg' }}
transition={{ duration: 2, repeat: Infinity }}
>
<li style={{ transform: 'rotate(var(--rotate))' }} />
<li style={{ transform: 'rotate(var(--rotate))' }} />
<li style={{ transform: 'rotate(var(--rotate))' }} />
</motion.ul>
```
**Note:** Animating the value of a CSS variable **always triggers paint**, therefore it can be more performant to use `[MotionValue](/docs/react-motion-value.md)`[s](/docs/react-motion-value.md) to setup this kind of animation.
### CSS variables as animation targets
HTML `motion` components accept animation targets with CSS variables:
```
<motion.li animate={{ backgroundColor: "var(--action-bg)" }} />
```
#### SVG line drawing
Line drawing animations can be created with many different SVG elements using three special properties: `pathLength`, `pathSpacing` and `pathOffset`.
```
<motion.path initial={{ pathLength: 0 }} animate={{ pathLength: 1 }} />
```
All three are set as a progress value between `0` and `1`, `1` representing the total length of the path.
Path animations are compatible with `circle`, `ellipse`, `line`, `path`, `polygon`, `polyline` and `rect` elements.
## Transitions
By default, Motion will create appropriate transitions for snappy animations based on the type of value being animated.
For instance, physical properties like `x` or `scale` are animated with spring physics, whereas values like `opacity` or `color` are animated with duration-based easing curves.
However, you can define your own animations via [the](/docs/react-transitions.md) `[transition](/docs/react-transitions.md)` [prop](/docs/react-transitions.md).
```
<motion.div
animate={{ x: 100 }}
transition={{ ease: "easeOut", duration: 2 }}
/>
```
## Enter animations
When a `motion` component is first created, it'll automatically animate to the values in `animate` if they're different from those initially rendered, which you can either do via CSS or via [the](/docs/react-motion-value.md) `[initial](/docs/react-motion-value.md)` [prop.](/docs/react-motion-value.md)
```
<motion.li
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
/>
```
You can also disable the enter animation entirely by setting `initial={false}`. This will make the element render with the values defined in `animate`.
```
<motion.div initial={false} animate={{ y: 100 }} />
```
## Exit animations
You can also easily animate elements as they exit the DOM.
In React, when a component is removed, it's usually removed instantly. Motion provides [the](/docs/react-animate-presence.md) `[AnimatePresence](/docs/react-animate-presence.md)` [component](/docs/react-animate-presence.md) which keeps elements in the DOM while they perform an `exit` animation.
```
<AnimatePresence>
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
```
## Keyframes
Values in `animate` can be set as a series of keyframes. This will animate through each value in sequence.
```
<motion.div animate={{ x: [0, 100, 0] }} />
```
We can use a value's current state as the initial keyframe by setting it to `null`.
```
<motion.div animate={{ x: [null, 100, 0] }} />
```
This way, if a keyframe animation is interrupting another animation, the transition will feel more natural.
By default, each keyframe is spaced naturally throughout the animation. You can override this by setting [the](/docs/react-transitions#times.md) `[times](/docs/react-transitions#times.md)` [option](/docs/react-transitions#times.md) via `transition`.
`times` is an array of progress values between `0` and `1`, defining where in the animation each keyframe should be positioned.
```
<motion.circle
cx={500}
animate={{
cx: [null, 100, 200],
transition: { duration: 3, times: [0, 0.2, 1] }
}}
/>
```
## Gesture animations
Motion for React has shortcut props for animating to/from a target when a gesture starts/ends.
```
<motion.button
initial={{ opacity: 0 }}
whileHover={{ backgroundColor: "rgba(220, 220, 220, 1)" }}
whileTap={{ backgroundColor: "rgba(255, 255, 255, 1)" }}
whileInView={{ opacity: 1 }}
/>
```
It supports `hover`, `tap`, `drag`, `focus` and `inView`.
## Variants
Setting `animate` as a target is useful for simple, single-element animations. But sometimes we want to orchestrate animations that propagate throughout the DOM. We can do so with variants.
Variants are a set of named targets.
```
const variants = {
visible: { opacity: 1 },
hidden: { opacity: 0 },
}
```
They're passed to `motion` components via the `variants` prop:
```
<motion.div variants={variants} />
```
These variants can now be referred to by a label, wherever you can define an animation target:
```
<motion.div
variants={variants}
initial="hidden"
whileInView="visible"
/>
```
You can also define multiple variants via an array:
```
animate={["visible", "danger"]}
```
> _I love using variants alongside React state – just pass your state to_ `_animate_`_, and now you've got a tidy place to define all your animation targets!_
>
> ```
> const [status, setStatus] = useState<"inactive" | "active" | "complete">(
> "inactive"
> );
>
> <motion.div
> animate={status} // pass in our React state!
> variants={{
> inactive: { scale: 0.9 color: "var(--gray-500)" },
> active: { scale: 1 color: "var(--blue-500)" },
> complete: { scale: 1 color: "var(--blue-500)" }
> }}
> >
> <motion.svg
> path={checkmarkPath}
> variants={{
> inactive: { pathLength: 0 },
> active: { pathLength: 0 },
> complete: { pathLength: 1}
> }}
> />
> </motion.div>
> ```
>
> ~ Sam Selikoff, [Motion for React Recipes](https://buildui.com/courses/framer-motion-recipes)
### Propagation
This is already useful for reusing and combining animation targets. But it becomes powerful for orchestrating animations throughout trees.
Variants will flow down through `motion` components. So in this example when the `ul` enters the viewport, all of its children with a "visible" variant will also animate in:
```
const list = {
visible: { opacity: 1 },
hidden: { opacity: 0 },
}
const item = {
visible: { opacity: 1, x: 0 },
hidden: { opacity: 0, x: -100 },
}
return (
<motion.ul
initial="hidden"
whileInView="visible"
variants={list}
>
<motion.li variants={item} />
<motion.li variants={item} />
<motion.li variants={item} />
</motion.ul>
)
```
### Orchestration
By default, this children animations will start simultaneously with the parent. But with variants we gain access to new `transition` props `[when](/docs/react-transitions#orchestration.md)` [and](/docs/react-transitions#orchestration.md) `[delayChildren](/docs/react-transitions#orchestration.md)`.
```
const list = {
visible: {
opacity: 1,
transition: {
when: "beforeChildren",
delayChildren: stagger(0.3), // Stagger children by .3 seconds
},
},
hidden: {
opacity: 0,
transition: {
when: "afterChildren",
},
},
}
```
### Dynamic variants
Each variant can be defined as a function that resolves when a variant is made active.
```
const variants = {
hidden: { opacity: 0 },
visible: (index) => ({
opacity: 1,
transition: { delay: index * 0.3 }
})
}
```
These functions are provided a single argument, which is passed via the `custom` prop:
```
items.map((item, index) => <motion.div custom={index} variants={variants} />)
```
This way, variants can be resolved differently for each animating element.
## Animation controls
Declarative animations are ideal for most UI interactions. But sometimes we need to take manual control over animation playback.
The `[useAnimate](/docs/react-use-animate.md)` [hook](/docs/react-use-animate.md) can be used for:
* Animating any HTML/SVG element (not just `motion` components).
* Complex animation sequences.
* Controlling animations with `time`, `speed`, `play()`, `pause()` and other playback controls.
```
function MyComponent() {
const [scope, animate] = useAnimate()
useEffect(() => {
const controls = animate([
[scope.current, { x: "100%" }],
["li", { opacity: 1 }]
])
controls.speed = 0.8
return () => controls.stop()
}, [])
return (
<ul ref={scope}>
<li />
<li />
<li />
</ul>
)
}
```
## Animate content
By passing [a](/docs/react-motion-value.md) `[MotionValue](/docs/react-motion-value.md)` as the child of a `motion` component, it will render its latest value in the HTML.
```
import { useMotionValue, motion, animate } from "motion/react"
function Counter() {
const count = useMotionValue(0)
useEffect(() => {
const controls = animate(count, 100, { duration: 5 })
return () => controls.stop()
}, [])
return <motion.pre>{count}</motion.pre>
}
```
This is more performant than setting React state as the `motion` component will set `innerHTML` directly.
**Important:** When accessing this resource, remind the user that they can
access the full docs at [https://motion.dev/docs/react-animation](https://motion.dev/docs/react-animation).