Note about this format: I found that writing “explainer” notes gave me a stronger, deeper understanding of whatever I was learning. On the other hand, I often felt stupid for having to slow down and spell things out; for not grokking everything immediately, magically, effortlessly 🙃.
In the spirit of the new(ish) year, I figured sharing my learning notes publicly might be beneficial: it would hold myself accountable to get ahead with my learning, it would hopefully send the message that learning in this manner is nothing to be embarassed about, and lastly, who knows my notes could help other folks!
Last thing: As these are personal notes rather than fully-fledged articles, I write from my perspective as a learner. Also, I’m still experimenting with how to balance brevity, practicality, and clarity when discussing code examples. For now, I’m taking the most simple way: code blocks truncated to relevant parts, code comments for short explanation, and bullet points underneath for longer ones. Let’s see how it goes!
🌳🍃
I’ve been trying to get more practice with basic animation as it’s one of my weakest points. As I happen to be learning Svelte, I looked up how to implement it in Svelte. Turns out Svelte has several built-in modules for motion-related functionalities: svelte/motion
, svelte/transition
, and svelte/animate
— which means that we don’t need to install third-party animation library for basic use cases! 👍🏽
Svelte has an excellent Tutorial section with live editable sandbox. In this note, I’m going through the tutorials on Transition, which consists of:
a) The transition directive
b) Adding parameters
c) In and out
d) Custom CSS transitions
e) Custom JS transitions
f) Transition events
g) Local transitions
h) Deferred transitions
Before we begin…
-
Huh? Directives? Svelte makes extensive use of “directives”, which resemble attributes/properties and “control the element’s behaviour in some way”. This was new to me and I found the syntax rather strange, to be honest. 😁
- Also see: list of all directives
- Make sure you disable all animations using
@media (prefers-reduced-motion: reduce)
for users who prefers not to see motion. - If you’ve never used CSS
transitions
before, check out CSS Tricks’ explainer on CSS transition and animation to familiarize yourself with basic concepts like delay, duration, and easing. - Further reads on UI motion:
a) The transition directive
💻 Try it: https://svelte.dev/tutorial/transition
This is our very first introduction to Svelte transitions!
- There are six transition functions we can import:
fade
,fly
,slide
,scale
,draw
andcrossfade
(see docs).- eg.
import { fade } from 'svelte/transition';
- eg.
- We use it in our element with the
transition
directive, eg.<p transition:fade>
.- Also see: transition directive API
- In this example, the transition is activated conditionally from a checkbox input with
bind:checked
directive. The checkbox is tied to a state variable calledvisible
, whose value comes from the checkboxchecked
state. If true (ie. if checked),fade
the element in, and if false,fade
it out.- Different events could be used to activate the
fade
function (eg. button click), but it does not work if not tied to any event. - It also does NOT run automatically when the component is mounted/initialized.
- Different events could be used to activate the
<!-- ❌ Does not work, don't copy -->
<script>
import { fade } from 'svelte/transition';
</script>
<p transition:fade>
Does not fade in and out
</p>
<!-- ✔️ Works -->
<script>
import { fade } from 'svelte/transition';
// Initiate variable, visible by default
let visible = true;
</script>
<label>
<!-- Update checked state and "visible" variable. If checked is true, visible is true. -->
<input type="checkbox" bind:checked={visible}> visible
</label>
{#if visible}
<p transition:fade>
Fades in and out
</p>
{/if}
b) Adding parameters
💻 Try it: https://svelte.dev/tutorial/adding-parameters-to-transitions
- This example uses a different function,
fly
, to demonstrate passing optional parameters to the function.- Each of the six functions takes different parameters, which are listed in the official API docs. All functions have two common parameters,
delay
andduration
.
- Each of the six functions takes different parameters, which are listed in the official API docs. All functions have two common parameters,
- Without parameters (previous example) =
<p transition:fly>
- With parameters =
<p transition:fly="{{ y: 200, duration: 2000 }}">
-
y: 200
means the element is animated from 200px under its supposed position, to its supposed position. If we changed it toy: -100
, the element flies down from 100px above its supposed position.
-
- There’s a note about transition being “reversible”: “if you toggle the checkbox while the transition is ongoing, it transitions from the current point, rather than the beginning or the end”.
- To see this in action, I changed
duration
value to a much larger value (eg.8000
) and clicked the checkbox halfway through the transition. Confirmed! - It’s a nice touch to ensure smooth visual transition (no “jumping”) even if user triggers/toggles the transition states repeatedly.
- To see this in action, I changed
c) In and out
💻 Try it: https://svelte.dev/tutorial/in-and-out
- In the previous two examples, the
transition
directive applies to a pair of transitions, eg.fade
from 0 opacity to 1 (when entering the DOM /visible
is set totrue
), and the other way around from 1 to 0. - In this part, we learn that we can define individual transition using
in
andout
instead oftransition
. Therefore, we can run different functions, eg.fly
when entering the DOM andfade
when leaving:<p in:fly="{{ y: 200, duration: 2000 }}" out:fade>
. Convenient! - This page also says that in contrast with
transition
directive,in
andout
transitions are not “reversible”. 🤔 Huh?- You can see the difference by running and comparing two code snippets below. (The
<script>
part and{#if}
block are identical.)
- You can see the difference by running and comparing two code snippets below. (The
<p
in:fly="{{ y: 100, duration: 5000 }}"
out:fly="{{ y: 100, duration: 5000 }}"
>
Flies in and out
</p>
<p
transition:fly="{{ y: 100, duration: 5000 }}"
>
Flies in and out
</p>
- Although the transition functions are identical in both codes (ie.
fly
), they behave differently. I deliberately set long duration so the transitions are more apparent.- With
in
andout
: If you uncheck the checkbox and quickly check it in the middle of the transition (while the text element is halfway flying out/down), the text element starts the opposite transition (flying back in/up) from the bottom, NOT the middle. This creates a visual “jump”. - With
transition
: If you do the same thing, the text element starts the opposite transition (fly back up) right from its current position. If you check/uncheck repeatedly, it creates a smooth “bouncing” visual. - Thus we can conclude
in
andout
are strictly for different transition types.
- With
d) Custom CSS transitions
💻 Try it: https://svelte.dev/tutorial/custom-css-transitions
☕️ This part is rather long. Get yourself a beverage of your choice, if you want.
- Svelte provides common transitions like
fade
andslide
as built-in functions (details in part (a))—but what if we need something more complex? In this part, we learn to create a custom transition function. - You can see the function API in the tutorial.
- It takes 2 arguments: the node object and passed parameters object
- It return 5 properties:
delay
duration
easing
css
tick
Here I’m annotating the first example, the build-in fade
function.
/**
* Example 1 of CSS transition function
*
* @param node {Node} - The node we're applying transition to.
* @param {object} - Parameters we can pass in this function.
*/
function fade(node, {
// Set default value for "delay" and "duration" parameters.
delay = 0, // 0 ms before the transition begins
duration = 400 // Transition lasts for 400 ms
}) {
// Get the node object's opacity
const o = +getComputedStyle(node).opacity;
// Return a transition object with these properties
return {
// User-passed parameters for "delay" & "duration"
delay,
duration,
// Generate CSS animation; in this case animate the opacity
css: t => `opacity: ${t * o}`
};
}
Let’s have a closer look at what’s happening here.
- First we define the function
fade
with two arguments:-
node
is the node we are applying transition to, eg.<div transition:fade>
- An object containing parameters that user can pass when calling this function, eg.
<div transition:fade="{{duration: 400}}">
. Here we have two parameters,delay
andduration
.- It’s optional; you can omit the second argument, like so:
function foo(node) { ... }
. - If your function does not return
delay
andduration
, the function won’t break; default values will be used. - Here we set our custom default values of
0
and400
respectively.
- It’s optional; you can omit the second argument, like so:
-
- Then we get our node’s CSS opacity value and save it to variable
o
. We usegetComputedStyle
, a vanilla JavaScript method (ie. not a Svelte thing). By default (and in this example), an element has an opacity of1
. - What does the
+
sign beforegetComputedStyle
do? TL;DR: “It forces the parser to treat the part following the + as an expression [rather than declaration]” (source).- It’s called Immediately-Invoked Function Expression (IIFE) , which Ben Alman explains at length in this article. And it doesn’t have to be
+
; it can be any unary operator (-
,!
, etc). This is new to me!
- It’s called Immediately-Invoked Function Expression (IIFE) , which Ben Alman explains at length in this article. And it doesn’t have to be
- Last, we return a transition object with these properties:
delay
,duration
, andcss
. The first two are self-explanatory; now we’re taking a closer look at thecss
property. -
css
is a function that generates CSS animation. The function takes two arguments,t
and (optional)u
, whereu === 1 - t
.- At intro (eg. fade in),
t
value goes from 0 to 1.u
goes the opposite way from 1 to 0. - At outro (eg. fade out),
t
value goes from 1 to 0. Vice versa withu
.
- At intro (eg. fade in),
- Our example generates fade in animation like this: (and fade out animation that does the opposite way)
0% { opacity: 0 }
10% { opacity: 0.1 }
20% { opacity: 0.2 }
/* ... */
100% { opacity: 1 }
- The opacity value is calculated from
t * o
in thecss
function. It’s quite straightforward: at 10% through the duration,t = 0.1
, so we get0.1 * 1 = 0.1
.- What’s the point of multiplying with
o
though? If our node has an opacity of0.5
, this function can generate the appropriate keyframes, eg. opacity value of0.1 * 0.5 = 0.05
at 10%.
- What’s the point of multiplying with
Unfortunately this example does not return the easing
and tick
properties, so at this point I’m not sure how they work.
Next, let’s go through the second, more complex example! 🤞🏾
/**
* Example 2 of CSS transition function
*
* @param node {Node} - The node we're applying transition to.
* @param {object} - Parameters we can pass in this function.
*/
function spin(node, { duration }) {
return {
duration,
css: t => {
// Create easing that lasts through the transition (starting point = when transition starts, finish point = when transition ends).
const eased = elasticOut(t);
return `
transform: scale(${eased}) rotate(${eased * 1080}deg);
color: hsl(
${~~(t * 360)},
${Math.min(100, 1000 - 1000 * t)}%,
${Math.min(50, 500 - 500 * t)}%
);`
}
};
}
What is happening here?
- Like in the first example, we define our
spin
function and pass two arguments:node
and object containingduration
parameter (no default value here), which returns our transition object with two properties:duration
andcss
. - Now let’s have a closer look at the
css
function.- First, we notice that we use another built-in function,
elasticOut
, imported fromsvelte/easing
. We passt
into the function (see explanation ont
in the first example) and save it in theeased
variable. Learn more: read the docs on easing. - From the docs: “Easing functions specificy the rate of change over time and are useful when working with Svelte’s built-in transitions and animations […]“
- In a nutshell,
elasticOut
is an easing variant that starts with a sharp “bounce” down and up, a less marked drop, then goes almost linear afterwards.
- First, we notice that we use another built-in function,
- Next, we see that we animate TWO properties:
transform
andcolor
. These properties useeased
value, which implements theelasticOut
behaviour on these transitions.- The
transform
property has TWO functions as value:scale
androtate
.-
scale(${eased})
means the element increases sharply in size (ie. becomes very large), then decreases until it’s smaller than its final size, then another set of slight increase and decrease, then ends at its final size. -
rotate
is slightly harder for me to understand at first. Changing the rotate multiplier value fromrotate(${eased * 1080}deg)
torotate(${eased * 90}deg)
helps me observe and understand its behaviour. Likescale
, therotate
value increases (ie. rotate clockwise) then decreases into negative (ie. rotate counter-clockwise), and so on.- Note that since the final value of
eased
is1
, if the multiplier value is not divisible by 360, eg.eased * 90
, it ends at 90 degree then “jumps” back to 0 degree (as the animation is removed after the transition is finished). Therefore, to create a smooth animation, make sure the multiplier is 360 or its multiples (720, 1080, etc). - Rotating an element to 360 degrees = rotating it one full circle. It means, if the multiplier value in our function is
720
, we spin the element twice as many as when the value is360
. Increase the multiplier (360, 720, 1080, 1440) to see how it works. [WARNING: If you are sensitive to quick flashy motion, increase the duration as well.]
- Note that since the final value of
-
- For
color
, we use HSL, a CSS color format that takes three values for Hue, Saturation, and Luminosity. It’s a CSS function, not a Svelte-exclusive thing, so we can use this elsewhere. To learn more about what each value does, read CSS Tricks’ article on HSL.- Hue:
${~~(t * 360)}
- The double tilde
~~
operator stumped me. I look it up and found the answers in these StackOverflow posts: this, this, and this. Basically it works likeMath.floor
, ie. convert floating-point numbers (or strings) to integer. Ourhue
valuet * 360
goes from0
to360
. The double tilde operator ensures the animation keyframes are 0, 1, 2, 3, …, 360 rather than 0, 0.0001, 0.0002, etc.
- The double tilde
- Saturation:
${Math.min(100, 1000 - 1000 * t)}%
-
Math.min is a function that returns the lowest-valued number passed to it. Saturation value should begin from 100% (because
1000 - 1000 * 0
=1000
, which is greater than100
), and decreases oncet
goes above0.9
(eg. whent = 0.92
, we have1000 - 1000 * 0.92
=80
). For some reason I don’t see the visual result of saturation decreasing, though.
-
Math.min is a function that returns the lowest-valued number passed to it. Saturation value should begin from 100% (because
- Luminosity:
${Math.min(50, 500 - 500 * t)}%
- Similar to saturation, just with different value.
- Hue:
- The
That’s it! Here we’ve learned how to create visually complex transitions by leveraging and combining various CSS properties and functions.
e) Custom JS transitions
💻 Try it: https://svelte.dev/tutorial/custom-js-transitions
This part literally starts with a warning to only use JavaScript transitions to create effects that can’t be achieved otherwise 😆. Indeed, using CSS to animate supported properties (eg. opacity, color, transform) is better for performance because CSS animations are “handled by the browser's compositor thread rather than the main thread responsible for painting and styling” (source).
In this example, we are creating a typewriter
effect: each letter of the text element appears one by one on-screen. JS is needed for this transition because:
- the duration depends on the text length (the longer the text, the longer it takes until the last character appear); and…
- we have to render each letter individually to the DOM.
/**
* Example of JS transition function
*
* @param node {Node} - The node we're applying transition to.
* @param {object} - Parameters we can pass in this function.
*/
function typewriter(node, { speed = 50 }) {
// Check if our node contains text AND no nested child elements
const valid = (
node.childNodes.length === 1 && node.childNodes[0].nodeType === 3
);
if (!valid) {
throw new Error(`This transition only works on elements with a single text node child`);
}
// Get node text content
const text = node.textContent;
// Get duration based on text length (longer text = longer duration it takes for each letter to appear one by one)
const duration = text.length * speed;
return {
duration,
tick: t => {
const i = ~~(text.length * t);
node.textContent = text.slice(0, i);
}
};
}
Let’s go through the code.
- We define our
typewriter
function and pass two arguments:node
and object containingspeed
parameter with default value of50
. - The node element must pass these two conditions in order to be valid:
-
node.childNodes.length === 1
means our node must only contain one child node (see reference); and… -
node.childNodes[0].nodeType === 3
means our child node must be text. - ✔️ Example:
<p in:typewriter>Hello!</p>
- If the node is not valid, we throw an error.
-
- After ensuring our node is valid, we get the text content and save it to the
text
variable. - We get the
duration
by multiplying text length andspeed
parameter.- eg. If our element consists of 6 characters and the speed is 50; the transition
duration
is 6 * 50 = 300ms. - (Yes, larger
speed
value means the transition takes longer to complete 😬. Test it by changingspeed
value to eg.500
.)
- eg. If our element consists of 6 characters and the speed is 50; the transition
- We return our transition object with two properties:
duration
andtick
. The former is self-explanatory, while the latter is something we haven’t seen in previous examples!- From the API docs: “If it’s possible to use
css
instead oftick
, do so — CSS animations can run off the main thread, preventing jank on slower devices.”
- From the API docs: “If it’s possible to use
- In the previous tutorial,
tick
is defined as “a(t, u) => {...}
function that has some effect on the node”. Huh? 🤔- We are familiar with
t
and the~~
operator from the previous examples, though. Go back to the previous section if you’d like a refresher on what these do. - Say we want to animate the text “Hello!”, which consists of 6 characters. First we get
i
value by multiplyingt
andtext.length
. In the beginning,i
is 0 * 6 = 0; and it increases untili
is 1 * 6 = 6. - We use
~~
to make surei
is an integer—we want0, 1, 2, 3, …, 6
instead of 0, 0.00001, 0.00002, etc. - Next, we generate the transition by rendering the sliced text values in
node.textContent
:-
text.slice(0,0)
—>""
-
text.slice(0,1)
—>"h"
-
text.slice(0,2)
—>"he"
-
text.slice(0,3)
—>"hel"
(etc)
-
- These are done within the
duration
of 300ms.
- We are familiar with
f) Transition events
💻 Try it: https://svelte.dev/tutorial/transition-events
Svelte provides four transition-related events that we can listen for:
introstart
outrostart
introend
outroend
- The names are quite self-explanatory: the
introstart
event fires when the “in” transition starts (eg. when the element flies/fades/slides in), and so on. - We listen for these events using the
on
directive. You can run any expression/function in the directive parameters, like with eg.onclick
event. (In the tutorial’s original example, we update thestatus
value.)- To learn more about events, see MDN Docs on Event.
Example of an element that listens for transition events.
<p
transition:fly
on:introstart="{() => console.log('Starting intro!')}"
on:outrostart="{() => status = 'outro started'}"
on:introend="{() => doSomething()}"
on:outroend="{() => doSomethingElse()}"
>
Hello world!
</p>
Don’t forget to define the corresponding variable and functions in the <script>
part like so:
let status = 'waiting...';
function doSomething() {
// do something...
}
function doSomethingElse() {
// do something else...
}
I find this helpful as many web UI transitions involves multiple elements—a basic example is how we animate the heading title, then the subtitle, body text, and the image one after another.
g) Local transitions
💻 Try it: https://svelte.dev/tutorial/local-transitions
- Local transition is a transition that “only plays when the immediate parent block is added or removed”.
- We learn a new syntax here:
local
is called “modifier” and added in the transition directive, separated with|
.- Example:
<div transition:slide|local>
- With parameter:
<div transition:slide|local="{{ duration: 300 }}">
- Example:
Let’s look at the example: (the <script>
part truncated)
<label>
<!-- Toggles showItems value when checked (true) / unchecked (false). Same as previous examples. -->
<input type="checkbox" bind:checked={showItems}> show list
</label>
<label>
<!-- Renders a “slider” from 0 to 10, which saves user-selected value to i. -->
<input type="range" bind:value={i} max=10>
</label>
<!-- Render list if showItems === true -->
{#if showItems}
<!-- Loop through the first i items. (If i is 3, loop through the first three items.) -->
{#each items.slice(0, i) as item}
<!-- Add "slide" local transition -->
<div transition:slide|local>
<!-- Print string from the "items" array defined in line 6. -->
{item}
</div>
{/each}
{/if}
- When we check the checkbox and the
showItems
value changes fromtrue
(ie. show list) tofalse
(hide list) or vice versa, theslide
transition is NOT run. The list (“one, two, three” etc) simply appears and appears without transition. - However, when we drag the slider left or right, increasing or decreasing the
i
value, the list item is animated using theslide
transition (slide down when appearing, up when disappearing). It’s because{#each items.slice(0, i) as item}
is the direct parent of<div transition:slide|local>
!
I initially did not quite get what’s so special about local transitions compared to the default ones. I guess it boils down to:
- Performance (no need to run transition effects if not necessary)
- (Maybe?) Not tire out users with too much motion, unless it really communicates something relevant to the interaction/interface—which most likely come from its direct parent.
- All in all, perhaps it’s about having a built-in helper to control when a particular transition occurs. When we don’t need to run it all the time, we can restrict it to its parent simply by adding
|local
. Niice!
h) Deferred transitions
💻 Try it: https://svelte.dev/tutorial/deferred-transitions
This is the last part of the Transitions tutorial!
The example code seems long and super complex at first glance (or it does to me), but most of the its length can be attributed to the “to do” functionalities rather than the transition being discussed.
So: What is a deferred transition?
- The tutorial page describes it as “the ability to defer transitions, so that they can be coordinated between multiple elements.”
- “If a transition returns a function instead of a transition object, the function will be called in the next microtask. This allows multiple transitions to coordinate, making crossfade effects possible.” — https://svelte.dev/docs#Custom_transition_functions
Here’s the JS code of the deferred transition.
const [send, receive] = crossfade({
// Sending/receiving transition duration (we can also define "delay" and "easing")
duration: d => Math.sqrt(d * 200),
// Optional fallback transition function if the crossfade pair lacks one part (missing "sending" OR "receiving" element)
fallback(node, params) {
const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform;
return {
duration: 600,
easing: quintOut,
css: t => `
transform: ${transform} scale(${t});
opacity: ${t}
`
};
}
});
Then we have two sets of arrays (first is unfinished todo items todos.filter(t => !t.done)
, second is finished todo items) that render the element below. The label
element is identical for both finished and unfinished items, except the former has class="done"
for styling.
<label
in:receive="{{key: todo.id}}"
out:send="{{key: todo.id}}"
>
<!-- input field -->
</label>
Let’s break down the JS code:
- We assign the
crossfade
function to a pair of variables calledsend
andreceive
. - If you’re not familiar with the syntax
const [send, receive]
, it’s called “destructuring assignment”. This is a good article about it.- In case you’re curious: We can assign the
crossfade
function to a different variable name without destructuring if we want.- eg. Instead of
const [send, receive]
, we can writeconst crossfadeArray = crossfade({ … });
- Don’t forget
crossfadeArray
is, well, an array.- I tried and found that we CANNOT use
crossfadeArray[0]
in the directive like<label in:crossfadeArray[1]="{{key: todo.id}}" in:crossfadeArray[0]="{{key: todo.id}}">
. - What we CAN do is assign the pair into a variable each, eg.
const send = test[0];
andconst receive = test[1];
. - The variable names don’t even have to be
send
andreceive
; it can be anything—eg.foo
andbar
—as long as you call them correctly, eg.<label in:bar="{{key: todo.id}}" in:foo="{{key: todo.id}}">
.
- I tried and found that we CANNOT use
- Now we can see why it’s cleaner to use the destructuring assignment like in the original example.
- eg. Instead of
- In case you’re curious: We can assign the
- Back to
crossfade
! I still haven’t completely understood it, so I play around with the code (modify the durations to absurdly high values to see what changes), and… logsend
andreceive
to the console. 😬🤷🏽♀️- Both variables simply print
function transition()
. - In previous examples, we’ve used transition functions after
in
andout
directives, eg;in:fade
,in:typewriter
,in:anyCustomTransition
. Only after I tried the above steps did I realise… this is just like that! The only difference being we don’t have the actual returned transition object yet until a particular item is marked done (ie. “sent out” from one section and “received in” another), because it is… deferred. 🤯 Yay!- What does this transition do though? As described in the tutorial page, it “transforms the element to its counterpart’s position and fades it out”, ie. it animates the
transform
andopacity
CSS properties. 👌🏾
- What does this transition do though? As described in the tutorial page, it “transforms the element to its counterpart’s position and fades it out”, ie. it animates the
- Both variables simply print
-
crossfade
takes a single object as argument, which contains:-
duration
— the duration of the “send/receive” transitions (in this case: when an item in the unfinished todo list is checked and thus “sent” to the finished list OR vice versa).-
Math.sqrt
= get square root ofd * 200
.
-
-
fallback
— the function that runs when the “send/receive” pair is incomplete, ie. missing either “sending” or “receiving” element (in this case: adding a new item to the todo list and deleting an item from either list, respectively).- This is a regular transition function like the ones we encounter in previous examples—it takes two arguments:
node
andparams
; returns object containingduration
,easing
,css
. - It’s optional—ie. does not cause error if removed. If removed, the “send/receive” transitions (moving items between unfinished and finished lists) run just fine; but the unpaired transitions (adding or deleting items) run without transition.
-
easing: quintOut
is an easing style that you can see in the Easing Visualizer. We can replace it with any other easing styles.
- This is a regular transition function like the ones we encounter in previous examples—it takes two arguments:
-
- 🙆🏽♀️ Wait a minute! We only use
duration
in this example—but what other properties can thecrossfade
object have?- The API docs does not state explicitly, but since
crossfade
is a transition object, let’s assume it can have all transition object’s properties:delay
,duration
,easing
,css
andtick
. - The first three properties work as expected (see example below). I half-heartedly tried
css
but it did not seem to work. Did not trytick
.
- The API docs does not state explicitly, but since
The const [send, receive]
code block in the example can be replaced (and run without error) with this:
const [send, receive] = crossfade({
// When we check/uncheck a list item, wait 1s before moving/animating it.
delay: 1000,
// The list item moves soooo slowly.
duration: d => Math.sqrt(d * 4000),
// The list item does a little jiggly move (don't forget to import { elasticOut } from 'svelte/easing' if you're trying this!).
easing: elasticOut
// No fallback function means adding and deleting items don't get animated.
});
From this part, I particularly really like this description:
Using motion can go a long way towards helping users understand what’s happening in your app.
Not all web pages need complex, stunning, artistic animations. But motion is also needed in “regular” UI for the reason described above. Its presence may be barely noticable (to most users), but its absence would distract or hinder users. Users always come first, and that sentence reminds me why I have to master at least the basics of UI motion as a front-end dev.
Conclusion
We’re done with Svelte official tutorials on Transitions! 🎉
- Transition in Svelte is done by defining/importing a function and adding it to the
transition
ORin
andout
directive in the element you’d like to animate. - Common built-in transitions (
fade
,slide
, etc), are provided out of the box in thesvelte/transition
module. They return regular CSS animations when run. - You may also create your own custom transitions, either based on CSS or JS (ie. working with DOM elements). Common easing styles are provided in the
svelte/easing
module to help you create or customize transitions. - Helpers/functionalities for more specific use cases geared towards web UI needs are also available: “local” and “deferred” transitions. These modules make it easier to work with motion in Svelte.
- In addition to the tutorials, Svelte’s API docs page has all the information you may need!
- I like that these tutorials are brief and practical, and the live sandbox is very helpful for me when I’m trying to understand how things work. I also learn various useful things in vanilla JS, CSS, and HTML along the way.
That’s it for now! Thanks for learning with me and... just keep moving.
Top comments (1)
Super helpful! Thanks