# Orbit Images

> Photos that travel along an SVG path with real depth, passing behind and in front of center content. Drag to spin, click to bring one forward.

Images ride an SVG path (ellipse, circle, star, heart, infinity, wave or your own) and scale to fit any container. With depth, photos at the back shrink, dim and pass behind the center content, while photos at the front grow and pass in front of it. Drag or swipe sideways to spin and fling the orbit, hover to slow it down, and click a photo to glide it to the front.

<demo-orbit-images>



</demo-orbit-images>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import OrbitImages from '~/components/ui/orbit-images/OrbitImages.vue';

  const images = [
    'https://picsum.photos/300/300?grayscale&random=1',
    'https://picsum.photos/300/300?grayscale&random=2',
    { src: 'https://picsum.photos/300/300?grayscale&random=3', alt: 'Bridge' },
  ];

  function onItemClick(index: number, src: string) {
    console.log(index, src);
  }
</script>

<template>
  <div class="h-115 w-full">
    <OrbitImages
      :images="images"
      :radius-x="340"
      :radius-y="80"
      :item-size="90"
      :depth="0.6"
      show-path
      height="100%"
      @item-click="onItemClick"
    >
      <span class="text-4xl font-semibold">Moments</span>
    </OrbitImages>
  </div>
</template>
```

The default slot is the center content. With `depth` above 0 the images pass behind and in front of it, and the front half of the path line is drawn over it.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        images
      </code>
    </td>
    
    <td>
      <code>
        Array<string | { src: string; alt?: string }>
      </code>
    </td>
    
    <td>
      <code>
        []
      </code>
    </td>
    
    <td>
      Images that travel along the path, as URLs or objects with their own alt text.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        alt-prefix
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Orbiting image'
      </code>
    </td>
    
    <td>
      Prefix for the alt text of images given as plain URLs.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shape
      </code>
    </td>
    
    <td>
      <code>
        'ellipse' | 'circle' | 'square' | 'rectangle' | 'triangle' | 'star' | 'heart' | 'infinity' | 'wave' | 'custom'
      </code>
    </td>
    
    <td>
      <code>
        'ellipse'
      </code>
    </td>
    
    <td>
      Shape of the path the images follow.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        custom-path
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Your own SVG path, drawn in a <code>
        base-width
      </code>
      
       by <code>
        base-width
      </code>
      
       space. Used when <code>
        shape
      </code>
      
       is <code>
        custom
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        base-width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1400
      </code>
    </td>
    
    <td>
      Size of the coordinate space for the radii, the item size and custom paths.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius-x
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        700
      </code>
    </td>
    
    <td>
      Horizontal radius for the ellipse, rectangle, infinity and wave.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius-y
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        170
      </code>
    </td>
    
    <td>
      Vertical radius for the ellipse, rectangle, infinity and wave.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        300
      </code>
    </td>
    
    <td>
      Radius for the circle, square, triangle, star and heart.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        star-points
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        5
      </code>
    </td>
    
    <td>
      Number of points on the star.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        star-inner-ratio
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Inner radius of the star as a fraction of the outer one.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rotation
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        -8
      </code>
    </td>
    
    <td>
      Rotation of the whole path in degrees. Images stay upright.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        40
      </code>
    </td>
    
    <td>
      Seconds for one full lap.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        item-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        64
      </code>
    </td>
    
    <td>
      Size of each image, in the same units as the radii.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        direction
      </code>
    </td>
    
    <td>
      <code>
        'normal' | 'reverse'
      </code>
    </td>
    
    <td>
      <code>
        'normal'
      </code>
    </td>
    
    <td>
      Which way the images travel.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fill
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Spreads the images evenly around the path. Turn it off to make them follow each other in a train.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        depth
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Sense of depth, from 0 to 1. Back images get smaller, dimmer and pass behind the center content.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        align-to-path
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Turns each image to follow the curve instead of staying upright.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        item-radius
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      <code>
        12
      </code>
    </td>
    
    <td>
      Corner radius in <code>
        item-size
      </code>
      
       units, or any CSS length. Half of <code>
        item-size
      </code>
      
       makes circles.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shadow
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      Strength of the soft shadow under each image, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hover-scale
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.08
      </code>
    </td>
    
    <td>
      How much an image grows when hovered.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        hover-speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      Speed while the orbit is hovered, as a fraction of the normal speed. 0 pauses it, 1 keeps full speed.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        draggable
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Lets people drag or swipe sideways to spin the orbit, and fling it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        click-to-front
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Clicking an image glides it around to the front.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-path
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Draws the path the images follow.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        path-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'rgba(128,128,128,0.35)'
      </code>
    </td>
    
    <td>
      Color of the path line.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        path-width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      Width of the path line in pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        path-dash
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Dash length of the path line in pixels. 0 draws a solid line.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        easing
      </code>
    </td>
    
    <td>
      <code>
        'linear' | 'easeIn' | 'easeOut' | 'easeInOut'
      </code>
    </td>
    
    <td>
      <code>
        'linear'
      </code>
    </td>
    
    <td>
      Easing applied to each lap.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        paused
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Stops the orbit. Dragging still works.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Images fan out from the front around the path, and the line draws in, when the orbit first comes into view.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        width
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      <code>
        '100%'
      </code>
    </td>
    
    <td>
      Width of the container. The orbit scales to fit.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        height
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      <code>
        'auto'
      </code>
    </td>
    
    <td>
      Height of the container. <code>
        auto
      </code>
      
       matches the shape of the orbit, any other value fits the orbit inside.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the container.
    </td>
  </tr>
</tbody>
</table>

## Events

<table>
<thead>
  <tr>
    <th>
      Event
    </th>
    
    <th>
      Payload
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        item-click
      </code>
    </td>
    
    <td>
      <code>
        (index: number, src: string)
      </code>
    </td>
    
    <td>
      Fired when an image is clicked. When bound, the images become keyboard-focusable buttons.
    </td>
  </tr>
</tbody>
</table>

## Slots

<table>
<thead>
  <tr>
    <th>
      Slot
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        default
      </code>
    </td>
    
    <td>
      Center content. With <code>
        depth
      </code>
      
      , images pass behind and in front of it.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- Without an `item-click` listener the orbit is decorative and marked `aria-hidden`.
- With `item-click`, every image is a `role="button"` with `tabindex="0"` and its alt text as label. Enter or Space activates it, and keyboard focus glides the image to the front with a visible focus ring.
- With `prefers-reduced-motion: reduce` the orbit stays still and the intro is skipped. Dragging still works.
- The loop sleeps while the orbit is offscreen.
- Ported from [React Bits](https://reactbits.dev/animations/orbit-images) by David Haz.
