From 5abc4aa77a71ba306c4a4b48e6d35577962cf584 Mon Sep 17 00:00:00 2001
From: Fuwn <contact@fuwn.me>
Date: Fri, 9 Feb 2024 05:14:43 -0800
Subject: feat(lazy): lazy visibility component

---
 src/lib/Lazy.svelte | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)
 create mode 100644 src/lib/Lazy.svelte

(limited to 'src/lib/Lazy.svelte')

diff --git a/src/lib/Lazy.svelte b/src/lib/Lazy.svelte
new file mode 100644
index 00000000..8bb180ac
--- /dev/null
+++ b/src/lib/Lazy.svelte
@@ -0,0 +1,49 @@
+<script lang="ts">
+	import { onMount, onDestroy } from 'svelte';
+
+	export let top = 0;
+	export let bottom = 0;
+	export let left = 0;
+	export let right = 0;
+	export let steps = 100;
+	export let threshold = 0.01;
+	export let once = false;
+
+	let element: Element;
+	let percent: number = 0;
+	let visible = false;
+	let observer: IntersectionObserver;
+
+	const intersectPercent = (
+		entries: IntersectionObserverEntry[],
+		observer: IntersectionObserver
+	) => {
+		for (let entry of entries)
+			if (entry.target === element) {
+				percent = Math.round(entry.intersectionRatio * 100);
+				visible = entry.intersectionRatio >= threshold;
+
+				if (visible && once) observer.unobserve(element);
+
+				break;
+			}
+	};
+
+	onMount(() => {
+		if ('IntersectionObserver' in window) {
+			let options = {
+				rootMargin: `${top}px ${right}px ${bottom}px ${left}px`,
+				threshold: Array.from({ length: steps }, (_, i) => i / (steps - 1))
+			};
+
+			observer = new IntersectionObserver(intersectPercent, options);
+			observer.observe(element);
+		}
+	});
+
+	onDestroy(() => observer?.unobserve(element));
+</script>
+
+<div bind:this={element}>
+	<slot {visible} {percent} />
+</div>
-- 
cgit v1.2.3

