Displacement

An image or painted canvas that bends around the pointer like wet glass, with a colour split and grain.

Behaviour

  • Reduced motion: the plain image, no bending.
  • A trail point follows the pointer on a spring; the gap between them drags nearby pixels, the colour channels split along the drag and grain shows while it moves.
  • Draws only while the trail moves and pauses off screen: a still page costs nothing. Props are read live.
  • WebGL2 with one full-screen triangle and no dependencies. Without WebGL2, or if the context is lost, the plain image or canvas shows.

Props

PropTypeDescription
srcstringThe image to distort. Same-origin or CORS-enabled, or the plain image shows instead.
altstringdefault ""Accessible name. Empty marks it decorative.
paint(ctx, width, height) => voidDraw the content on a 2D canvas instead, in CSS pixels. Called on mount and every resize.
radiusnumberdefault 0.22Size of the bending area, as a share of the shorter side.
strengthnumberdefault 0.6How far pixels are dragged with the pointer. 0 turns it off.
aberrationnumberdefault 0.5How far the red and blue channels split along the drag, 0 to 1.
grainnumberdefault 0.08Film grain over the moving area, 0 to 1.
classNamestringSize it here: it fills its box.

Installation

npx shadcn@latest add https://purrfectui.com/r/displacement.json

How to use

import { Displacement } from "@/components/ui/displacement";

<Displacement src="/hero.jpg" alt="Mountains at dusk" className="h-96 rounded-3xl" />

// or paint it yourself
<Displacement className="h-64" paint={(ctx, w, h) => { ctx.font = "600 96px sans-serif"; ctx.fillText("Hello", 24, h / 2); }} />

Source code

Loading…

License

MIT. Copy it into your project and change it as you like.

0