# Morph Slider

An image slider drawn on one WebGL canvas. Each change of slide blends the two images through a displacement shader, with a short RGB split at the midpoint and a slow idle drift on the shown image. Use the arrows, the dots or the Left/Right keys, or drag the image sideways to scrub the morph by hand. Release past 40% to finish the change, or earlier to snap back.

<demo-morph-slider>



</demo-morph-slider>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import MorphSlider from '~/components/ui/morph-slider/MorphSlider.vue';
  import type { MorphItem } from '~/components/ui/morph-slider/types';

  const items: MorphItem[] = [
    { image: '/slides/one.jpg', caption: 'One' },
    { image: '/slides/two.jpg', caption: 'Two' },
    { image: '/slides/three.jpg', caption: 'Three' },
  ];
</script>

<template>
  <div class="h-125 w-full">
    <MorphSlider
      :items="items"
      transition="ripple"
      ease="power2.inOut"
      :duration="1.1"
      :intensity="0.55"
      autoplay
    />
  </div>
</template>
```

Images load with `crossOrigin = 'anonymous'`, so remote hosts must send CORS headers. A dark placeholder shows until each image loads.

## 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>
        MorphItem[]
      </code>
      
       (<code>
        { image; caption? }
      </code>
      
      )
    </td>
    
    <td>
      4 Unsplash shots
    </td>
    
    <td>
      Slides to morph between.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        start-index
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Slide shown on mount.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        transition
      </code>
    </td>
    
    <td>
      <code>
        'melt' | 'ripple' | 'shear' | 'swirl'
      </code>
    </td>
    
    <td>
      <code>
        'melt'
      </code>
    </td>
    
    <td>
      Character of the displacement morph. Ripple starts at the last pointer-down point.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        duration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.1
      </code>
    </td>
    
    <td>
      Length of a full transition in seconds.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ease
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'power2.inOut'
      </code>
    </td>
    
    <td>
      GSAP-style ease name (<code>
        power1
      </code>
      
      –<code>
        power4
      </code>
      
      , <code>
        sine
      </code>
      
      , <code>
        expo
      </code>
      
      , <code>
        circ
      </code>
      
      , <code>
        none
      </code>
      
       + <code>
        .in
      </code>
      
      /<code>
        .out
      </code>
      
      /<code>
        .inOut
      </code>
      
      ).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intensity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.55
      </code>
    </td>
    
    <td>
      How far the images warp during the morph.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        scale
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2.4
      </code>
    </td>
    
    <td>
      Frequency of the noise field (affects Melt most).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        aberration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.35
      </code>
    </td>
    
    <td>
      RGB split that peaks mid-transition.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        drift
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.4
      </code>
    </td>
    
    <td>
      Idle drift and breathing on the shown image.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        autoplay
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Advance automatically; pauses while hovered.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        autoplay-delay
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        4
      </code>
    </td>
    
    <td>
      Seconds each slide holds before autoplay advances (minimum 1).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        loop
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Wrap from the last slide back to the first.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        16
      </code>
    </td>
    
    <td>
      Corner radius in pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        overlay-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#000000'
      </code>
    </td>
    
    <td>
      Tint of the edge vignette.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-captions
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Show captions that blur in with each slide.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-controls
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Show the previous and next buttons.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-indicators
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Show the slide dots.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the root element.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The stage is focusable (`role="group"`, `aria-roledescription="carousel"`). Left and Right arrow keys change slides.
- The arrow buttons have labels. The dots use `role="tab"` with `aria-selected`.
- The caption region uses `aria-live="polite"`, so slide changes are announced.
- With `prefers-reduced-motion: reduce`, the displacement and RGB split are off, transitions cap at 0.4 s, and caption and dot transitions are instant.
- The render loop pauses when the slider leaves the viewport.
- Ported from [React Bits](https://reactbits.dev/components/morph-slider) by David Haz.
