Svelte Fundamentals

Motion & Transitions

Svelte Fundamentals

Check out a free preview of the full Svelte Fundamentals course

The "Motion & Transitions" Lesson is part of the full, Svelte Fundamentals course featured in this preview video. Here's what you'd learn in this lesson:

Rich discusses using motion stores, easing functions, the spring function, and transitions to update values gradually for smoother animations. Creating custom CSS transitions, custom JavaScript transitions, transition events, and local transitions are covered in this segment.


Transcript from the "Motion & Transitions" Lesson

>> Congratulations, we've made it through the first part of the tutorial. We're gonna get into the fun stuff now. We're gonna begin part two with a discussion of motion. So, so far we've been assigning to values inside our components and that's been causing the DOM to update immediately.

But sometimes we don't want that, sometimes we want values to update gradually over time, for example is progress bar here. It's cool that we can update the progress bar like that, but it would be nice if it smoothly animated to its new value and in Svelte we can do that with motion stores.

Here progress is a writable store that starts with the value of zero and every time we press one of these buttons, it sets a new value. But if we change that writeable to a tweened store like that and replace the Svelte store import with Svelte motion, now when we press the buttons, the bar animates smoothly.

Looks a little bit robotic at the moment and that's because we need to add an easing function. An easing function is a function that takes a value between zero and one and returns a value between zero and one. By default it's just this. This is the linear easing function that we saw before, but you can do something like we could square T like this and then it'll do some very different behavior.

We actually have a library of easing functions available in Svelte, these formula were devised by Ropana many years ago and we just have a whole stack of them. One of the most useful ones I find is called cubic out which kind of smoothly, it starts fast and then it smoothly transitions to the final state.

So let's replace that. Right, I find that to be quite a pleasing motion and we can set the duration to whatever we want. You can make it a super slow tween or a very fast tween depending on what it is we're using the motion for. There's a whole bunch of options that you can pass to the tweened store, including a custom interpolate function which allows you to interpolate non-numeric values.

For example, maybe you wanna transition between two colors. Well, you can pass a function that knows how to interpolate between two colors. And you can also pass those options when you call progress.set to override the default values. So tweening is one way to get motion, but another way that is more applicable in some circumstances is to use some rudimentary spring physics.

It works particularly well for values that are frequently changing such as the mouse position. If I move the mouse around here, you will see that the orange dot follows the cursor perfectly. And again, we can just replace the store that represents the coordinates of the mouse with a spring store.

Let's get rid of those, replace with spring and again, we're gonna import that from Svelte motion. And now when I move the mouse around, the circle kind of lags behind a little bit and moves in this pleasing smooth manner. Now, springs have some settings that you can apply to them.

They have a stiffness setting and a damping setting that determines how they respond to changes. And we can specify our own initial values. So for the spring value here, for the coordinates we'll specify a stiffness of 0.1, 1 would mean that the spring was perfectly stiff, 0 would mean that it's not stiff at all.

And we'll set a damping value of 0.25 like that. And now we see we get some very different and more springy behavior. And we can change those. And you can sort of get a feel for how the different values will affect the behavior of the spring. Another way that we can make user interfaces a little bit more appealing is by gracefully transitioning elements into and out of the DOM.

And Svelte has something that helps with this called the transition directive. So let's import the fade transition from Svelte transition. And this text here which becomes visible when the checkbox is checked and invisible when it's not, we can add the transition directive to that element. Transition:fade to reference the function that we just imported.

And now when we toggle this, you'll see that the text fades in and it fades out. And we can parametrize these transitions. So let's get rid of the fade and turn it into a fly transition. And we'll give it some values. We'll give it 200 wide pixels and a slow duration of 2000 milliseconds.

I'm gonna change the word fades to the word flies, so that is correct. And now when we toggle that, we're gonna see it fly in and out of the DOM. And what's interesting is that this transition is reversible if while it's transitioning out, we change our mind and bring it back.

Then it doesn't start from the end or start from the start. It starts from wherever it was at the moment that the state change happened. So this is a nice way to make transitions that almost feel like they're behaving as physical objects would within your user interface. Sometimes you want to have a transition that applies when the element enters the DOM and one that applies when it leaves the DOM that are separate from each other.

So for example, we might want this text to fly in but then fade out. And we can do that by using both transitions together, import fade. And then down here, we'll change the transition:fly into in:fly so that it applies when the element enters the DOM. Then we'll add out:fade, flies in, fades out.

Now, one thing that you'll notice is that this is not reversible, there's no way that we can make this reversible. So if we toggle the state rapidly, then it will start flying in from the beginning of the transition instead of trying to figure out what some intermediate position would be.

So far we've been using the built-in transitions, and it's useful to be able to import those, but we can also create our own because sometimes you will have some very specific requirements. The way that transitions work is, well, they're just functions. This is the fade transition that we were looking at earlier.

It's a function that receives the element in question together with the options that were passed in. And it returns this transition object which has some values like delay and duration. And most importantly, this CSS method which given a value between zero and one, return some CSS. Zero means it's the beginning of the intro or the end of the outro.

One means it's the element's final resting state once it's in the DOM. So in this case, we're setting the opacity to t multiplied by the elements native opacity which we get with getComputedStyle. So zero before the element has transitioned in, t will be zero, the opacity will also be zero.

One once the element is settled in the DOM, it'll be one times whatever that value was. And so we can create our own CSS method to create our own transition effects. And what Svelte will do is it will run that method at the beginning of the transition to get a list of keyframes.

So for the fade transition that we just saw, these are the keyframes that you will end up with. It will then put those keyframes into a style sheet and add the relevant CSS to the element in such a way that the actual animation is happening entirely in CSS.

So it's not running code on every frame and updating the style of that element, it's all being done in CSS. And the advantage of that is that it will frequently run off the main thread on the compositor, and it will do so in a way that doesn't block any other JavaScript from happening and isn't blocked by other JavaScript from happening.

This is useful for making your transition smooth and it's also better for your battery life and so on. Right, so let's make a really wacky transition. I'm gonna begin by importing the elastic out transition. And we're gonna create our own CSS method here. At the moment, it's not doing anything.

Let's turn this into a function that returns a whole block of CSS. We'll ease the value with elasticOut. And then transform it with a scale and a rotation. All right, and 1,080 degrees is 3 times 360, so we're gonna make this thing spin around three times.

And then the color is gonna use HSL. Which if you haven't used HSL before, it's a really nice way to declare colors because you're defining a hue which is a value between 0 and 360, where 0 and 360 are pure red. And then it kinda transitions through a rainbow.

S is the saturation and then L is the lightness. And it gives you a way to manipulate colors in a way that's a lot easier to predict and understand than when you're dealing with things like RGB values. Okay, so Math.trunc(t * 360). We're using .trunc to get an integer value, which the hue needs to be.

And then we're going to create a saturation which, don't worry too much about the details of this formula, it's just for effect. Right, and if I've done this right, then hopefully when we toggle the visible state we're gonna get a fun little transition. So you can do pretty much whatever you want here.

You have the full power of JavaScript but we're not actually using JavaScript to apply these styles. It's all happening in CSS. So we sort of get the best of both worlds. Sometimes you can't express everything in CSS. Sometimes you actually do need to run code on each frame of the animation in order to get the desired effect.

So for example, we might wanna create a typewriter effect where this text here becomes visible one character at a time. And we can do that by instead of returning a CSS method, we can return a tick function which applies some manipulation to the DOM. So here's our typewriter transition, again, we're just passing in the node that the transition is gonna be applied to together with some options.

I'm gonna grab a copy of the node's text. And the duration of this transition is gonna be based on the length of the text. So that's gonna be text.length divided by the speed expressed in, I'm actually not sure what this is in seconds. And we pass that duration back to Svelte so it knows how long this transition should take.

And then we're gonna create a tick function. Which is gonna be called on each frame. And "i" is gonna be the number of characters of the string that we should show. And we can calculate that with (text.length * t). Again, we're using Math.trunc because we need this to be an integer.

And we can set the textContent of the node to text.slice(0, i). And now when we toggle visible, the text is written on one character at a time. And because we're using transition here as opposed to in or out, this is also reversible just like our CSS transitions.

In some cases, it's useful to know when your transitions are starting and ending. And so Svelte dispatches transition events when you have an element that uses transitions. We have a status value here and we're just gonna assign to that value when these events happen. So the paragraph element here, we're gonna create some event listeners on:introstart.

We're gonna set the status to intro started. And I'm just gonna copy that line four times. We're gonna change the intro there to outro and we're gonna change the start here to end and we're gonna change that intro to outro. And now when we toggle the visibility of the element, we'll see that the outro started, the outro ended, the intro started and the intro ended.

It's not something that you use a lot but it's useful to have when you do need it. Okay, so ordinarily transitions will play on elements when any container block is added or destroyed. And so in the example here, these elements are removed when we change the length of the list which is what we want.

But they're also removed when we remove the list entirely. And in this case, let's say that's not what we want. We want the list to only appear and disappear when the checkbox is toggled. But we want the individual elements to transition out as we play with the slider.

And we can do that by turning the slide transition that's being applied to those elements into a local transition. Just like event modifiers, we add the pipe symbol to where the transition is declared and add the local modifier. And so now the transition continues to play when the length of the list changes.

But if I toggle this list, the whole thing will just disappear altogether.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now