UPDATE: This post is now over 2 years old, but it's still kinda relevant. This technique should work exactly the same with SvelteKit.
Ever come across websites with cool animations whenever navigating from one page to another? For example this website by Sarah Drasner. Usually, you'd use client-side libraries and clever CSS tricks to achieve these sort of things. Probably nothing wrong with that, but it adds to your website's overall download size, and your CSS will become hard to maintain as your site grows in complexity. However, thanks to Svelte, we can now do these cool things without any client-side library or CSS trickery:
So now we can have cool PWAs, that are server-side rendered, have smooth page transitions, and are compiled down to really tiny amounts of javascript. With no need for client-side libraries, virtual DOMs, or shuffling CSS classes around. Oouwee!
And with that, our lives are now complete. Because what else could we possibly want? We should now all go and praise Rich Harris the JavaScript God. Or do we need more JavaScript frameworks? Don't we have enough? I can't take it anymore. Make it stop. Aaaaaahhh this is the end.
How?
These animations all use Svelte's crossfade transition. If you haven't used it before, I suggest checking out the tutorial. It will teach you how to crossfade two elements within the same component. But we are interested in crossfading an element from one component into another (in Sapper, each page is a component). The trick to making the crossfade transition work across different pages (or components) defined in separate files, is to create the crossfade transition in its own module/file, and import
it into the different components. Check it out:
Here we create the crossfade transition in its own file and export the send
and receive
functions so that we can reuse them in different files.
// crossfade.js
import { quintOut } from 'svelte/easing';
import { crossfade } from 'svelte/transition';
const [send, receive] = crossfade({
duration: d => Math.sqrt(d * 300),
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}
`
};
}
});
export {send, receive};
And then we use the send
and receive
function.
// page1.svelte
<script>
import {send, receive} from './crossfade.js';
</script>
<main>
<h1 out:send="{{key: 'h1'}}" in:receive="{{key: 'h1'}}">Look, I'm above the image</h1>
<img out:send="{{key: 'borat'}}" in:receive="{{key: 'borat'}}" alt='Borat' src='great-success.png'>
</main>
And then use the same send
and receive
function again, making the crossfade work across components.
// page2.svelte
<script>
import {send, receive} from './crossfade.js';
</script>
<main>
<img out:send="{{key: 'borat'}}" in:receive="{{key: 'borat'}}" alt='Borat' src='great-success.png'>
<h1 out:send="{{key: 'h1'}}" in:receive="{{key: 'h1'}}">Now I am below the image</h1>
</main>
The container needs to be absolutely positioned, otherwise it won't work.
// global.css
main {
position: absolute;
width: 100%;
}
There you go. Thanks for reading.
Visit https://buhrmi.dev if you'd like professional support.
Top comments (10)
Hi, thanks for providing the changes and some examples.
It can lead to great effects in the UI, and it seems to be easy to achieve with what you've done.
I'm curious where did you get the idea? some other framework/library?
No, I was looking at this site and thought it'd be cool if there was a way to achieve this sort of effect with built-in Svelte tools only.
Okay, thanks!
This kind of animations are so cool!
If I follow the "inspiration chain", I see the influence of Sarah Drasner's work, I knew it ;-)
I once saw her live, back in 2017, and my mind was blown π€―:
Very interesting.
Got it to work in Svelte REPL: svelte.dev/repl/e5e3027d27cf4440a1...
Hey, is there something similar for SvelteKit? It seems like SvelteKit's layout system works differently.
Sorry for the late reply. It should work exactly the same for SvelteKit
Thanks, it looks like there's a SvelteKit bug that causes the page to get duplicated. I'll check again though.
Stefan, this is great stuff thanks for posting
this is hot, thanks for sharing
thanks for the post, I am having issues with the crossfade.js during deployment to now. the node_modules folder is in the .nowignore file as best practices? any ideas?