Skip to content

Instantly share code, notes, and snippets.

@harryqt
Created October 12, 2025 13:09
Show Gist options
  • Save harryqt/fe4f59a9e720b8dbc1767e715838fe49 to your computer and use it in GitHub Desktop.
Save harryqt/fe4f59a9e720b8dbc1767e715838fe49 to your computer and use it in GitHub Desktop.
Svelte 5 + Runed Scroll-Aware Moving Ad Block
<script>
import { onMount } from 'svelte';
import { useIntersectionObserver, ScrollState } from 'runed';
import { randomInt } from 'es-toolkit';
let boxes = Array.from({ length: 40 });
let adIndex = $state(8);
let scroll;
let wasIntersecting;
let target = $state(null);
let isIntersecting = $state(false);
onMount(() => {
scroll = new ScrollState({
element: window
});
});
useIntersectionObserver(
() => target,
(entries) => {
const entry = entries[0];
if (!entry || !scroll) return;
isIntersecting = entry.isIntersecting;
if (wasIntersecting && !entry.isIntersecting) {
if (scroll.directions.bottom) {
adIndex = (adIndex + randomInt(10, 12)) % boxes.length;
} else if (scroll.directions.top) {
let newIndex = (adIndex - randomInt(10, 12)) % boxes.length;
adIndex = newIndex < 0 ? (adIndex = 3) : (adIndex = newIndex);
}
isIntersecting = false;
wasIntersecting = false;
}
wasIntersecting = entry.isIntersecting;
}
);
</script>
<header class="sticky top-0 bg-white py-2 text-center shadow">
{#if isIntersecting}
Ad in view (index {adIndex})
{:else}
Moved → index {adIndex}
{/if}
</header>
<main class="flex flex-wrap items-center justify-center gap-6">
{#each boxes as _, i}
<article class="h-64 w-96 bg-amber-100"></article>
{#if i === adIndex}
<div bind:this={target} class="h-56 w-[728px] bg-blue-200 transition-all duration-300">
Ad block at index {adIndex}
</div>
{/if}
{/each}
</main>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment