mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-06-25 00:35:22 -04:00
Working fan mockup
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
<script lang="ts">
|
||||
interface Card {
|
||||
front: string;
|
||||
back: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
cards: Card[];
|
||||
fanDelay?: number;
|
||||
}
|
||||
|
||||
let { cards, fanDelay = 2500 }: Props = $props();
|
||||
|
||||
// Extract 4 cards (or fewer if not enough provided)
|
||||
let displayCards = $derived(cards.slice(0, 4));
|
||||
let totalCards = $derived(displayCards.length);
|
||||
|
||||
let fanned = $state(false);
|
||||
|
||||
$effect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
fanned = true;
|
||||
}, fanDelay);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="relative h-64 w-96">
|
||||
<!-- Cards start piled on left, fan out to right -->
|
||||
{#each displayCards as card, i (i)}
|
||||
{@const fanOffset = (totalCards - 1 - i) * 75}
|
||||
|
||||
<div
|
||||
class="absolute inset-0 flex items-center justify-center transition-all duration-700 ease-out"
|
||||
style:transform={fanned
|
||||
? `translateX(${fanOffset-100}px) rotate(${8 + i * (-16 / (totalCards - 1))}deg)`
|
||||
: "translateX(-100px) rotate(-8deg)"}
|
||||
style:z-index={totalCards - i}
|
||||
style:transition-delay={fanned ? `${i * 100}ms` : "0ms"}
|
||||
>
|
||||
<img
|
||||
src={card.front}
|
||||
alt="Card {i + 1}"
|
||||
class="max-h-64 w-auto object-contain drop-shadow-lg"
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -11,34 +11,22 @@
|
||||
let fanned = $state(false);
|
||||
|
||||
const cardDeck: Attachment<HTMLDivElement> = (node) => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
fanned = entry.isIntersecting;
|
||||
}
|
||||
},
|
||||
{ rootMargin: "-40% 0px -40% 0px", threshold: 0 }
|
||||
);
|
||||
|
||||
observer.observe(node);
|
||||
|
||||
const onEnter = () => (fanned = true);
|
||||
const onLeave = () => (fanned = false);
|
||||
|
||||
node.addEventListener("mouseenter", onEnter);
|
||||
node.addEventListener("mouseleave", onLeave);
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
node.removeEventListener("mouseenter", onEnter);
|
||||
node.removeEventListener("mouseleave", onLeave);
|
||||
const check = () => {
|
||||
const rect = node.getBoundingClientRect();
|
||||
const cardCenter = rect.top + rect.height / 2;
|
||||
fanned = cardCenter <= window.innerHeight / 2;
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", check, { passive: true });
|
||||
check();
|
||||
|
||||
return () => window.removeEventListener("scroll", check);
|
||||
};
|
||||
</script>
|
||||
|
||||
<div
|
||||
{@attach cardDeck}
|
||||
class="relative h-64 w-48 cursor-pointer"
|
||||
class="relative h-64 w-48"
|
||||
role="img"
|
||||
aria-label="Card deck"
|
||||
>
|
||||
@@ -47,7 +35,7 @@
|
||||
src={back}
|
||||
alt="Back"
|
||||
class="absolute inset-0 max-h-64 w-full object-contain drop-shadow-md transition-all duration-500 ease-in-out"
|
||||
style:transform={fanned ? "translateX(85px) rotate(5deg)" : "rotate(-2deg)"}
|
||||
style:transform={fanned ? "translateX(90px) rotate(4deg)" : "rotate(-2deg)"}
|
||||
style:z-index="1"
|
||||
/>
|
||||
<!-- Front card -->
|
||||
@@ -55,7 +43,7 @@
|
||||
src={front}
|
||||
alt="Front"
|
||||
class="absolute inset-0 max-h-64 w-full object-contain drop-shadow-md transition-all duration-500 ease-in-out"
|
||||
style:transform={fanned ? "translateX(-85px) rotate(-5deg)" : "rotate(2deg)"}
|
||||
style:transform={fanned ? "translateX(-90px) rotate(-4deg)" : "rotate(2deg)"}
|
||||
style:z-index="2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,22 +1,35 @@
|
||||
<script lang="ts">
|
||||
import FrontBack from "$lib/components/cards/FrontBack.svelte";
|
||||
import CardFan from "$lib/components/cards/CardFan.svelte";
|
||||
|
||||
const sampleCards = [
|
||||
{ front: "/cards/1_Corinthians_13_front.png", back: "/cards/1_Corinthians_13_back.png" },
|
||||
{ front: "/cards/Esther_4_front.png", back: "/cards/Esther_4_back.png" },
|
||||
{ front: "/cards/Psalms_front.png", back: "/cards/Psalms_back.png" },
|
||||
{ front: "/cards/Revelation_12_13_15_front.png", back: "/cards/Revelation_12_13_15_back.png" }
|
||||
];
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>BIBDLE Cards</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="text-center pb-72">Collectible Bible Verse Trading Cards</div>
|
||||
<div class="min-h-dvh py-10 px-4">
|
||||
<div class="text-center pb-96">Collectible Bible Verse Trading Cards</div>
|
||||
<div class="min-h-dvh py-10 px-4 overflow-x-hidden">
|
||||
<div class="max-w-3xl mx-auto">
|
||||
|
||||
|
||||
|
||||
<div class="flex justify-center">
|
||||
<h2 class="text-xl font-semibold mb-6">Card Fan Demo</h2>
|
||||
<p class="text-gray-400 mb-4">Cards will fan out after a few seconds...</p>
|
||||
<div class="flex justify-center mb-96">
|
||||
<CardFan cards={sampleCards} />
|
||||
</div>
|
||||
<div class="flex justify-center mb-96">
|
||||
<FrontBack
|
||||
front="/cards/Esther_4_front.png"
|
||||
back="/cards/Esther_4_back.png"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user