This package uses the Preact Options API to introduce an animate
prop to every native element (e.g. <div>
, <span>
, <button>
, etc.). It uses the motion/mini
package to animate the elements. To understand which features of Motion are supported, see this comparison table.
Choose your package manager, then install this package and the motion
package.
-
PNPM
pnpm add preact-in-motion motion
-
Bun
bun add preact-in-motion motion
-
Yarn
yarn add preact-in-motion motion
-
NPM
npm install preact-in-motion motion
Always import the package, so it can install itself into Preact at runtime.
import 'preact-in-motion'
Now you can define the animate
prop on any host element (e.g. <div>
, <span>
, <button>
, including SVG elements).
// Animate the opacity when a boolean changes on rerender.
<div animate={{
opacity: visible ? 1 : 0,
}}>
When the element's parent component is re-rendered, the keyframes will be diffed. If any keyframes are different, a new animation will be scheduled.
Animation options (e.g. duration
, ease
, etc) may be defined next to the keyframes. See the Motion docs for details.
import { easeInOut } from 'motion'
<div animate={{
opacity: visible ? 1 : 0,
duration: 1,
ease: easeInOut,
}}>
You may set a transition
function to customize the animation options for each style property.
<div animate={{
opacity: visible ? 1 : 0,
transform: `scale(${visible ? 1 : 0.5})`,
transition: prop => ({
duration: prop === 'opacity' ? 1 : 0.2,
}),
}}>
Alternatively, the transition
prop can be set to an object, with property-specific options.
// Identical to the previous example.
<div animate={{
opacity: visible ? 1 : 0,
transform: `scale(${visible ? 1 : 0.5})`,
transition: {
duration: 0.2,
opacity: { duration: 1 },
},
}}>
The following event-driven animations are supported:
initial
Style values to be applied before mounting.update
Animate when this element is re-rendered.enter
Animate when this element is added to the DOM.leave
Animate before this element is removed from the DOM. (must use<AnimatePresence>
)whileHover
Animate when this element is hovered on.whileFocus
Animate when this element is focused.whilePress
Animate when this element is pressed on.
<div animate={{
// Start out invisible.
initial: {
opacity: 0,
},
// Fade in when added to the DOM.
enter: {
opacity: 1,
},
// Scale up while hovered over.
whileHover: {
transform: 'scale(1.1)',
duration: 0.2,
},
}}>
To animate an element before unmounting it, you must wrap the element or its parent component with an <AnimatePresence>
element.
<AnimatePresence>
{visible && (
<h1
animate={{
// Fade out before unmounting.
leave: {
opacity: 0,
},
}}>
Fade Into Obscurity
</h1>
)}
</AnimatePresence>
If the children of AnimatePresence
could possibly be reordered or toggled (i.e. isHappy
in the example below), you must set a key
prop on each element. Note that conditionally mounting a child with {visible && (...)}
does not require a key
prop to be set (in this case, the child's index is used as the default key).
Additionally, you may use the enterDelay
prop on AnimatePresence
to force “enter animations” to wait. This delay is only applied when a “leave animation” is in progress, making it useful for smooth transitions between elements.
<AnimatePresence enterDelay={0.3}>
{isHappy ? (
<span
key="happy"
animate={{
initial: { transform: 'translateX(50px)', opacity: 0 },
enter: { transform: 'translateX(0)', opacity: 1 },
leave: { reverse: true },
}}>
😄 I'm happy!
</span>
) : (
<span
key="sad"
animate={{
initial: { transform: 'translateX(-50px)', opacity: 0 },
enter: { transform: 'translateX(0)', opacity: 1 },
leave: { reverse: true },
}}>
😢 I'm sad...
</span>
)}
</AnimatePresence>
You may set reverse: true
on the leave
prop to copy keyframes from the initial
prop.
Easing functions are provided by the motion
package.
-
Spring animations (docs)
import { spring } from 'motion' <div animate={{ transform: 'scale(1.1)', type: spring, bounce: 1, duration: 3, }}>
-
Easing functions (docs)
The
ease
prop accepts a function, pre-defined string, or a cubic bezier array (e.g.[0.25, 0.1, 0.25, 1]
).import { easeInOut } from 'motion' <div animate={{ transform: 'scale(1.1)', ease: easeInOut, }}>
These easing functions can be imported from
motion
:cubicBezier
easeIn
/easeOut
/easeInOut
backIn
/backOut
/backInOut
circIn
/circOut
/circInOut
anticipate
steps
MIT