Svelte(Kit): Making a reusable 'Animate On Scroll (once visible)' component
Designer, developer, and business owner focusing on building well rounded and usable sites with Svelte (Kit), Shopify (headless), Sanity, and Webflow.
Ever needed to animate in an element once it's visible on the screen? There are multiple ways to achieve this, with even more ways to handle the animation. I'm using
svelte-intersection-observer to track when the element is in view, and TailwindCSS classes for the animations.
Check out the code: <AnimatedElement>
<script>
import IntersectionObserver from "svelte-intersection-observer";
let element;
let intersecting;
export let duration = "700ms";
export let delay = "0";
export let fade = false;
export let fly = false;
export let offset = "0";
export let once = true;
// Set the default to fly if 'fly' or 'fade' are not specified
fly = !fade && !fly ? true : false;
</script>
<IntersectionObserver
rootMargin={`${offset}px`}
{once}
{element}
bind:intersecting
>
{#if fade}
<div
bind:this={element}
class={`${intersecting ? "opacity-100" : "opacity-0"} transition`}
style="transition-delay: {delay}; transition-duration: {duration}"
>
<slot {intersecting} />
</div>
{/if}
{#if fly}
<div
bind:this={element}
class={`${
intersecting ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-6"
} transform transition`}
style="transition-delay: {delay}; transition-duration: {duration}"
>
<slot {intersecting} />
</div>
{/if}
</IntersectionObserver>
I know this code could be shortened, but I'm not that concerned with that. I'd rather have a component I can easily read. Also note, I'm passing intersecting back into the slot, in case you need the trigger wherever you use the component
How to use:
<AnimatedElement>
<a href="/">
<img src="/img/logo.svg" class="h-16 md:h-20 lg:h-auto" alt="logo" />
</a>
</AnimatedElement>
Potential improvements
I didn't have time to lollygag when coding this, but I would like to make this without needing the extra wrapper div inside the <AnimatedElement> component. I don't see why this wouldn't be possible by using the <svelte:element this={tag}> tag and passing the element tag along with the class into the component. Like this <AnimatedElement tag="div" class="flex">. Let me know you'd like for me to code that out.
Thanks for reading! Now go grab some pizza!