Animation
The basic syntax for motion components deviates a bit from framer-motion. The equivalent to a motion.div
looks like this:
<Motion let:motion><div use:motion/></Motion>
The outer Motion
-component passes an action via a slot prop to the inner div
-element.
The reason to deviate is the design of Svelte. I’ve studied the differences between React and Svelte a bit and one observation is that Svelte offers quite a few syntactical goodies, which only work with DOM-elements. The list includes actions, class directives, events, transitions and some special bindings like ‘bind:group`. Thus, Component libraries should avoid taking away the DOM-elements from the user, in my opinion.
If you only want to animate a div, you can also use the MotionDiv
component, which corresponds to a motion.div
in framer-motion:
import {MotionDiv} from 'svelte-motion'
Basic Animation
You can easily animate components with the animate
, transition
and variants
props of the Motion
component. Application is completely equivalent to framer-motion
Here is a simple example (REPL):
<script>
import { Motion } from 'svelte-motion';
let i = 0;
</script>
<Motion animate={{ rotate: i }} transition={{ duration: .5 }} let:motion>
<div use:motion/>
</Motion>
<button on:click={ () => i+=45 }>turn</button>
Here is a bigger example. It shows the use of
- animation & transition prop
- alternative easing functions in transition prop
- keyframes
- variants
Dynamic variants, propagation and orchestration
You can control the order of animations in several ways. In the next example, the first list (1,2,3) is controlled via dynamic variants. You can control delay and other props of the variants with the custom
prop of the Motion
component.
On the second list it shows how animations propagate down to child elements. The outer Motion
-component on the ul
-element orchestrates the animation of its children.
- 1
- 2
- 3
- a
- b
- c
Exit Animation
You can animate the unmount of a component with an AnimatePresence
component. The syntax is slightly different compared to framer-motion. AnimatePresence
takes a prop list
, which should be an array of objects. All items in the array list
render one instance of the child of the AnimatePresence
. The objects should have a unique attribute key
. The object can be retained via the slot prop item
. Then you can use the exit prop in the child Motion
. Here is an example which demonstrates it:
The current syntax is more complicated than the stock-Svelte out-transition, but for orchestration of animations it may be worth the overhead.
Animation Controls
With useAnimation
you can create animation controls. These can be passed to one or more Motion
components into the animate
prop. With this, you can start and stop animations more easily in your code.
import { useAnimation } from 'svelte-motion'
const controls = useAnimation();
The start
method of the animation control object returns a promise. This makes it easy to create sequences of animation. In the example below, many boxes are grouped in a grid. When you click on one of the small boxes, all boxes fade out delayed in a circular way, starting at the box you clicked. To make this work, the custom
prop is used. The delay is distorted at random, so it is not trivial to determine, how long the animation takes. Awaiting the promise is a simple solution.
Animation Lifecycles
When using the animate
prop to add motion to your components, you can pass a callback to these three lifecycle props:
- onUpdate: called once per frame when animation is running. It provides latest values of the things you change in your
animate
prop, eg. if you are animatingopacity
andx
the callback could look like
const onUpdate = ({opacity,x}) => {//do something}
- onAnimationStart: called once the animation starts (after delay, if specified)
- onAnimationComplete: called when the animation is completed. Provides the definition of the animation or the name of the variant, so that you can see, what animation finished.
<Motion animate={"myVariant"} onComplete={(definition)=>{/*definition is "myVariant" */}}>...
<Motion animate={{x:100,opacity:0}} onComplete=={(definition)=>{/*definition is {x:100,opacity:0}*/}}/>...
This REPL demonstrate the usage of all three lifecycles.