Little Bits is a series of short articles explaining quick solutions to common dev problems. No unnecessary descriptions or code snippets. No bullshit.
In this episode, I want to show you how you can animate elements in your Svelte app on scroll.
Problem
When user is scrolling your Svelte app/website, we want to animate the entering elements to get that extra fancy feel.
The best solution is to use Intersection Observer, but we can also use a nice abstraction built on top of it - svelte-inview
. This is a small library I created some time ago that greatly simplify usage of the IO API. Let's see how we can do this.
Solution
Create an app with Vite
Let's use Vite to create a simple Svelte App:
npm create vite@latest my-svelte-app -- --template svelte
cd my-svelte-app
npm i
npm run dev
Add scaffolding
Let's add some simple app with basic styling. Paste that code in your App.svelte
file:
<script>
import AnimatedElement from './lib/AnimatedElement.svelte';
</script>
<main>
<div class="hero">
<h1>Hello Svelte Inview</h1>
<p>Scroll down to animate the elements</p>
</div>
<div class="full-height">
<AnimatedElement />
</div>
<div class="full-height">
<AnimatedElement />
</div>
</main>
<style>
:root {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
main {
text-align: center;
padding: 0 1em;
margin: 0 auto;
}
h1 {
margin-bottom: 0.5rem;
}
p {
margin-top: 0;
}
.hero {
height: calc(100vh - 16px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.full-height {
height: 100vh;
display: flex;
justify-content: center;
}
</style>
You probably notice that we import the AnimatedElement
component - we will create it in the next step.
Add AnimatedElement
component
Create a file called AnimatedElement.svelte
and add this code:
<script lang="ts">
import { fade } from 'svelte/transition';
import { inview } from 'svelte-inview';
let isInView;
</script>
<div
class="wrapper"
use:inview={{ unobserveOnEnter: true, rootMargin: '-20%' }}
on:change={({ detail }) => {
isInView = detail.inView;
}}
>
{#if isInView}
<div in:fade class="box">
<h3>Appears from nowhere</h3>
<p>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Error,
adipisci nihil iste.
</p>
</div>
{/if}
</div>
<style>
.wrapper {
margin-top: 30px;
}
.box {
width: 300px;
border: 1px solid rgb(221, 221, 221);
padding: 25px;
border-radius: 10px;
}
</style>
What is going on here?
First we import the inview
action and animation from svelte.
Then we assign the action to the wrapper with some options:
-
unobserveOnEnter: true
- this means that once element is on the screen and animated, it won't be animated again - `rootMargin: '-20%' - this means that element will be considered "on screen" once it'll reach 20% of the monitor - better explanation here
Next step is to handle the element entry - we assign the inView
flag from the action to our local variable - isInView
. This allows us to determine if element should be animated.
Then we check if isInView
is true, if it is we animate the element into view by adding the in:fade
action.
Useful links
-
svelte-inview
action - -
explanation of Intersection Observer API - which is used by
svelte-inview
internally - more about svelte actions
- more about in/out animations in svelte
Oldest comments (5)
Very usefull! I've been doing this kind of things by hand.
Awesome lib, thank you for sharing.
However this approach enforces you to wrap any of the stuff you want to animate into a component (here : AnimatedElement).
Is there any other way to integrate animation on scroll without this ?
Not sure what do you mean? It doesn't have to be a component. You can attach this action to any DOM element you want.
Although it might work, but it seems ugly ( enforces parent wrapper, variable for each element i want to animate)
I think it should be just doable using some svelte actions combined with some global styles for for animations
Well, it's very opinionated :) I don't think it's ugly :) It doesn't enforce the wrapper, it's just an approach I took here to make it more readable. Also, can you elaborate on
variable for each element i want to animate
? No idea what do you mean here :)I think it should be just doable using some svelte actions combined with some global styles for for animations
- please, do show me how would you handle that :)