Effortless element entrance/exit animations.
Stencil App Starter
Unlike most animation libraries, there's no new API to learn—just use CSS or the Web Animations API.
Here's a basic example using CSS. See Declarative Animations.
Item A Item B Item C
Here's a basic example using WAAPI. See Imperative Animations.
import createPresenceHandler from 'animate-presence'; const List = const fade = opacity: 0 1 ; const handleEnter = ; const handleExit = ; return <animate = = > <div ="item">Item A</div> <div ="item">Item B</div> <div ="item">Item C</div> </animate-presence> ;;
Declarative Animations (CSS)
As child elements are added or removed, data-enter
and data-exit
attributes are automatically applied.
Using CSS, you can use these attributes as hooks to apply an animation or transition.
Once the animation finishes, <animate-presence>
automatically cleans itself up, removing the attribute and any listeners.
Attributes
Attribute | Description |
---|---|
data-enter |
Applied immediately when a node enters. Trigger your entrance animation here. |
data-exit |
Applied when a node will be removed. Trigger your exit animations here. The node will be removed when the animation completes. |
Note Animate Presence overrides the default
animation-fill-mode
of animating children toboth
(rather thannone
). This provides more reasonable default behavior in most situations, so the rule is injected with a specificity (021
) high enough to override theanimation
shorthand for most rules (like.item[data-enter]
).
If you experience visual flickering at the start or end of an animation, you may be using a selector with a higher specificity than
021
. Theanimation
shorthand resets theanimation-fill-mode
tonone
, so you likely need to manually specifyboth
.
Stagger
When multiple elements enter/exit, Animate Presence automatically sets a custom property, --i
, on each child.
This makes staggered animations simple with a small calc
function.
[
Imperative Animations (WAAPI)
Using the Web Animations API to imperatively animate entrances/exits is the best way to coordinate complex, multi-step animations.
It's also possible to use your favorite animation library, like Popmotion or Anime.js!
Events
Event | Description |
---|---|
animatePresenceEnter |
Dispatched immediately when a node enters. Handle your entrance animation here. |
animatePresenceExit |
Dispatched when a node will be removed. Handle your exit animations here. The node will be removed when the handler returns. |
As you might expect, these events are dispatched to the children of animate-presence
as they enter or exit.
These events bubble, so listeners can be attached directly to an animate-presence
element via addEventListener
or onAnimatePresenceEnter/Exit
handlers.
createPresenceHandler
Use the included createPresenceHandler
utility to create async callbacks for these events.
; const onExit = ; const ap = document;ap;
Nesting
Animate Presence uses a tree-based approach, meaning that nested animate-presence
elements are aware of their parents and children.
Enter animations are applied top-down, meaning the top-level parent enters, which triggers the next child entrance, and so on.
Exit animations are applied bottom-up, meaning the deepest child exits, which triggers the next parent exit, and so on.
Animate Presence relies on
querySelectorAll
to construct the internal tree, so it does not (yet) work with Shadow Roots. This is an area I'm actively looking into.
@stencil/router
Usage withFor Stencil apps using @stencil/router
, Animate Presence has an <animated-route-switch>
component which allows you to smoothly animate between routes.
-
Swap your
<stencil-route-switch>
component with<animated-route-switch>
-
Due to a current bug in
@stencil/router
, you will need to useinjectHistory
to passlocation
down toanimated-route-switch
.Hopefully this will be fixed soon, but there's a simple work around for now.
;; injectHistoryAppRoot;
- Apply your animations on
[data-enter]
and[data-exit]
as usual.
As noted above, if your Stencil components rely on
shadow
encapsulation, Animate Presence won't work as expected (yet). I'm working on it. For now, you can either usescoped
encapsulation or stay tuned for updates!