# Infinite Spiral

> An endlessly looping 3D helix of images with adjustable motion, depth and spacing.

A looping helix of image cards. Cards wind around a vertical axis, grow near the centre and blur and fade at the ends. It can play by itself, follow a vertical drag, follow page scroll, or all three.

<demo-infinite-spiral>



</demo-infinite-spiral>

## Installation

```bash
npx shadcn-vue@latest add "https://nxui.geoql.in/r/infinite-spiral.json"
```

## Usage

```vue
<script setup lang="ts">
  import InfiniteSpiral from '~/components/ui/infinite-spiral/InfiniteSpiral.vue';
  import type { InfiniteSpiralItem } from '~/components/ui/infinite-spiral/types';

  const items: Array<string | InfiniteSpiralItem> = [
    'https://images.unsplash.com/photo-1781764177519-9c9c88733c3e?w=900',
    {
      src: 'https://images.unsplash.com/photo-1774009485852-13a515d32e36?w=900',
      alt: 'Fern',
      href: '/ferns',
    },
  ];
</script>

<template>
  <div class="relative h-155 w-full overflow-hidden">
    <InfiniteSpiral
      :items="items"
      animation-mode="all"
      :cards-per-turn="7"
      :grayscale="1"
    />
  </div>
</template>
```

The spiral fills its parent, so give the parent a height. Use at least as many items as `cards-per-turn` for a full turn.

## Props

<table>
<thead>
  <tr>
    <th>
      Prop
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        items
      </code>
    </td>
    
    <td>
      <code>
        Array<string | InfiniteSpiralItem>
      </code>
    </td>
    
    <td>
      <code>
        []
      </code>
    </td>
    
    <td>
      Image URLs or <code>
        { id?, src, alt?, href?, target?, label? }
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.55
      </code>
    </td>
    
    <td>
      Auto-play speed in cards per second.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        direction
      </code>
    </td>
    
    <td>
      <code>
        'up' | 'down'
      </code>
    </td>
    
    <td>
      <code>
        'up'
      </code>
    </td>
    
    <td>
      Auto-play direction.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        animation-mode
      </code>
    </td>
    
    <td>
      <code>
        'auto' | 'drag' | 'scroll' | 'all'
      </code>
    </td>
    
    <td>
      <code>
        'auto'
      </code>
    </td>
    
    <td>
      Plays by itself, follows a drag, follows page scroll, or all.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        170
      </code>
    </td>
    
    <td>
      Helix radius in pixels. Shrinks on narrow containers.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        card-width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        100
      </code>
    </td>
    
    <td>
      Card width in pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        card-height
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        100
      </code>
    </td>
    
    <td>
      Card height in pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        vertical-spacing
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        60
      </code>
    </td>
    
    <td>
      Vertical distance between cards in pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        perspective
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1000
      </code>
    </td>
    
    <td>
      Perspective distance in pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        cards-per-turn
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        7
      </code>
    </td>
    
    <td>
      Cards in one full turn of the helix.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rotation
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Extra rotation of the helix in degrees.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        card-tilt
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      In-plane tilt of every card in degrees.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        card-radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        10
      </code>
    </td>
    
    <td>
      Card corner radius in pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        center-scale
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.2
      </code>
    </td>
    
    <td>
      Scale of the card at the centre of the helix.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        edge-fade
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      Part of the helix that fades out at each end (0–1).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        edge-blur
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        6
      </code>
    </td>
    
    <td>
      Largest blur in pixels on cards near the ends.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pause-on-hover
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Pause auto-play while the pointer is over the spiral.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        image-fit
      </code>
    </td>
    
    <td>
      <code>
        'cover' | 'contain' | 'fill' | 'none' | 'scale-down'
      </code>
    </td>
    
    <td>
      <code>
        'cover'
      </code>
    </td>
    
    <td>
      CSS <code>
        object-fit
      </code>
      
       of the images.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        grayscale
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Grayscale amount of the images (0–1).
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The stage is a `role="list"`; each card is a list item named by its `label` or `alt`. Cards with an `href` are links and keep the browser focus ring.
- A drag that moves the spiral does not trigger a click on the card under the pointer.
- With `prefers-reduced-motion: reduce` auto-play stops. Drag and scroll still move the spiral because the user drives them.
- Ported from [React Bits](https://reactbits.dev/components/infinite-spiral) by David Haz.
