# 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.

> Live preview: see it play at https://bettertyped.github.io/react-cards-opening/docs/guides/origin-and-landing/
>
> Here both are the card's own place on the shelf.

## From a button to a counter

```tsx
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 `launch` element, scaled so it is as wide as that element.
- It ends at the centre of the `landing` element, 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.
- `landing` is 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

```tsx
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.

```tsx
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" }),
  };
}}
```

> `release` runs when the card has landed and also when the run was stopped halfway. Use it to undo whatever `lead` did.

## 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.

## Related

- [CardOpening](https://bettertyped.github.io/react-cards-opening/docs/components/card-opening/)
- [ScanGrid](https://bettertyped.github.io/react-cards-opening/docs/components/scan-grid/)
- [Flight](https://bettertyped.github.io/react-cards-opening/docs/effects/flight/)
- [Collection example](https://bettertyped.github.io/react-cards-opening/docs/examples/collection/)

Source: https://bettertyped.github.io/react-cards-opening/docs/guides/origin-and-landing/
