# Halftone Reveal

> A halftone print of an image whose dots swell into the real photo under the cursor.

Prints an image as a halftone screen in mono, duotone or full color. Move the pointer over it and the dots swell into the real photo, leaving a trail that shrinks back into print. Click to send a ring of color out through the page.

<demo-halftone-reveal>



</demo-halftone-reveal>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import HalftoneReveal from '~/components/ui/halftone-reveal/HalftoneReveal.vue';
</script>

<template>
  <div class="relative h-150 w-full overflow-hidden">
    <HalftoneReveal
      src="/images/portrait.png"
      fit="contain"
      mode="duotone"
      shape="dot"
      :cell-size="7"
      :angle="22"
      ink-color="#2442ff"
      accent-color="#ff48a5"
      paper-color="#ffffff"
      :misregistration="0.4"
      :reveal-radius="160"
    />
  </div>
</template>
```

The image must be served with CORS headers. Transparent or plain backgrounds are detected and left unprinted. Set `paper-color` to your page background so the print sits on the page.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        src
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'https://images.unsplash.com/...'
      </code>
    </td>
    
    <td>
      Image to print. Transparent or plain backgrounds are left unprinted. Must be served with CORS headers.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fit
      </code>
    </td>
    
    <td>
      <code>
        'contain' | 'cover'
      </code>
    </td>
    
    <td>
      <code>
        'cover'
      </code>
    </td>
    
    <td>
      Show the whole image, or crop it to fill the container.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mode
      </code>
    </td>
    
    <td>
      <code>
        'mono' | 'duotone' | 'color'
      </code>
    </td>
    
    <td>
      <code>
        'mono'
      </code>
    </td>
    
    <td>
      One ink, two inks at different screen angles, or full color: CMYK on light paper and RGB on dark paper.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shape
      </code>
    </td>
    
    <td>
      <code>
        'dot' | 'ellipse' | 'square' | 'diamond' | 'line' | 'cross'
      </code>
    </td>
    
    <td>
      <code>
        'dot'
      </code>
    </td>
    
    <td>
      Shape of the halftone marks.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        cell-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        6
      </code>
    </td>
    
    <td>
      Spacing of the halftone screen, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        angle
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        45
      </code>
    </td>
    
    <td>
      Rotation of the screen, in degrees.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dot-scale
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Scales every mark. Below 1 keeps them apart, above 1 lets them merge sooner.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ink-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#120f17'
      </code>
    </td>
    
    <td>
      Color of the printed marks.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        accent-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ff4f2a'
      </code>
    </td>
    
    <td>
      Second ink used in duotone mode.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        paper-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Background the image is printed on. Match it to your page.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contrast
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.1
      </code>
    </td>
    
    <td>
      Tonal contrast applied before printing.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        brightness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Shifts the image lighter or darker before printing.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        invert
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Prints a negative of the image.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        roughness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Gives the marks ragged, ink-spread edges, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        misregistration
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Offsets the ink layers like a slightly misaligned press, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reveal-radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        160
      </code>
    </td>
    
    <td>
      Radius of the area around the cursor where the dots swell into the real photo, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        softness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      Width of the band where the dots grow into the photo. 0 is a hard edge.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        linger
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.2
      </code>
    </td>
    
    <td>
      Seconds the revealed trail takes to shrink back into print. 0 turns the trail off.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reverse
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Start as the photo and print wherever the cursor goes.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        wander
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Lets the reveal drift around on its own while the pointer is away.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        click-burst
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Clicking sends a ring of color rippling out through the print.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Grows the dots in from the center on load.
    </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>
  
  <tr>
    <td>
      <code>
        style
      </code>
    </td>
    
    <td>
      <code>
        StyleValue
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Inline styles for the root element.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The canvas is decorative and marked `aria-hidden`. If the image carries meaning, give the parent an `aria-label` or a visible caption.
- With `prefers-reduced-motion: reduce` the intro is skipped and `wander` is disabled. The reveal still follows the pointer.
- The render loop sleeps when idle, offscreen or in a hidden tab. GL resources are freed on unmount.
- Needs WebGL 2. Without it the container stays empty.
- Ported from [React Bits](https://reactbits.dev/animations/halftone-reveal) by David Haz.
