Origin and landing
A reveal starts somewhere and ends somewhere, and both are yours to choose. launch names the element a card comes out of. landing names the one it shrinks into. Neither has to be part of this library: any element on the page will do.
Here both are the card's own place on the shelf.
From a button to a counter
const pack = useRef<HTMLButtonElement>(null);
const counter = useRef<HTMLSpanElement>(null);
<button ref={pack} onClick={() => hunt.start()}>Open the pack</button>
<span ref={counter}>{opened.length} cards</span>
<CardOpening
opening={hunt.opening}
launch={() => pack.current}
landing={() => counter.current}
/>How the card uses them
- The card starts at the centre of the
launchelement, scaled so it is as wide as that element. - It ends at the centre of the
landingelement, scaled to that element's width, and fades over its last 0.18 seconds. - Both are measured at the moment they are needed, so a page that scrolled in between is still right.
landingis asked for up to thirty frames in a row. A slot that only exists once the card is opened has time to render.
A slot per card
const slots = useRef(new Map<string, HTMLElement>());
{cards.map((card) => (
<li key={card.id} ref={(element) => element && slots.current.set(card.id, element)}>
<Card card={card} faceDown={!opened.includes(card.id)} width="100%" />
</li>
))}
<CardOpening opening={hunt.opening} landing={(card) => slots.current.get(card.id) ?? null} />Play something first
Return an object from launch and you can put a GSAP timeline in front of the card. It is added to the reveal's own timeline, so skipping and stopping work through it without extra code.
launch={(card, { tone, plan }) => {
const pack = packRef.current;
if (!pack) return null;
const lead = gsap.timeline();
lead.to(pack, { scale: 1.06, duration: 0.2, yoyo: true, repeat: 3 });
if (plan.rays) lead.to(pack, { boxShadow: `0 0 60px 10px ${tone}`, duration: 0.3 });
return {
element: pack,
lead,
release: () => gsap.set(pack, { clearProps: "scale,boxShadow" }),
};
}}Nowhere
Without launch the card grows out of the middle of the screen. Without landing, or when it answers null, the card fades out where it is. Both are fine for a reveal that is the whole point of the screen.