DEV Community

Cover image for Animate on scroll with Svelte Inview - Little Bits
Maciek Grzybek
Maciek Grzybek

Posted on • Edited on

Animate on scroll with Svelte Inview - Little Bits

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.

Gif showing the scroll animation

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


Enter fullscreen mode Exit fullscreen mode

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>


Enter fullscreen mode Exit fullscreen mode

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>


Enter fullscreen mode Exit fullscreen mode

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

Top comments (4)

Collapse
 
maciekgrzybek profile image
Maciek Grzybek

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 :)

Collapse
 
val1nk profile image
Valentin SOURICE

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 ?

Collapse
 
maciekgrzybek profile image
Maciek Grzybek

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.

Collapse
 
ucielsola profile image
Uciel

Very usefull! I've been doing this kind of things by hand.