Pixel wipe
A second picture builds in over the first pixel by pixel, in a springy wave that starts wherever the pointer comes in, and pulls back out the same way.
- enhancer
- swap
- image
- pixels
- hover
- reveal
- since 0.2
Background Studio: tune it below, then copy the code
Your system asks for reduced motion: the demo shows its final state and nothing moves.
Live demo
Pixel wipe example
Code
It follows the controls above: change an option and the code changes with it.
<script src="https://search.axi0.io/axi0/motion/motion.js" defer></script>
<div class="m-pw-demo" data-motion="pixel-wipe"><svg viewBox="0 0 420 280" width="420" height="280" role="img" aria-label="A green valley by day"><defs><linearGradient id="m-pw-d" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#6cc6ff"/><stop offset="1" stop-color="#dff3ff"/></linearGradient></defs><rect width="420" height="280" fill="url(#m-pw-d)"/><circle cx="320" cy="70" r="34" fill="#fff1b8"/><path d="M0 190 Q90 120 190 176 T420 150 V280 H0z" fill="#4fae6a"/><path d="M0 232 Q120 180 250 226 T420 214 V280 H0z" fill="#2f7d4a"/><path d="M60 120 q20 -14 40 0 q16 -10 30 2 q-36 6 -70 -2z" fill="#fff"/></svg><svg viewBox="0 0 420 280" width="420" height="280" role="img" aria-label="The same valley at night"><defs><linearGradient id="m-pw-n" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#070b2a"/><stop offset="1" stop-color="#3a2d74"/></linearGradient></defs><rect width="420" height="280" fill="url(#m-pw-n)"/><circle cx="320" cy="70" r="30" fill="#ffe9b0"/><circle cx="332" cy="62" r="27" fill="#0d1235"/><circle cx="60" cy="50" r="2" fill="#fff"/><circle cx="130" cy="90" r="1.5" fill="#fff"/><circle cx="210" cy="40" r="2" fill="#fff"/><circle cx="250" cy="110" r="1.4" fill="#fff"/><circle cx="390" cy="140" r="1.6" fill="#fff"/><path d="M0 190 Q90 120 190 176 T420 150 V280 H0z" fill="#1d2f52"/><path d="M0 232 Q120 180 250 226 T420 214 V280 H0z" fill="#101a33"/><circle cx="150" cy="214" r="3" fill="#f6a04d"/><circle cx="300" cy="236" r="3" fill="#f6a04d"/></svg></div>
import { PixelWipe } from "./motion/react/PixelWipe";
<PixelWipe>Your content</PixelWipe>
<script setup lang="ts">
import PixelWipe from "./motion/vue/PixelWipe.vue";
</script>
<template>
<PixelWipe>Your content</PixelWipe>
</template>
const el = document.querySelector("#title");
el.dataset.motion = "pixel-wipe";
The wrappers: PixelWipe.tsx (React, TypeScript) and PixelWipe.vue (Vue). Both render the same attributes as the HTML; the loader does the rest.
Options
| Attribute | Default | What it does |
|---|---|---|
data-motion-cell | 22 | Pixel size |
data-motion-speed | 1 | How fast the wave travels |
data-motion-toggle | false | Swap on click or tap only (the pointer passing over does nothing) |
Accessibility
Both children stay in the page; the second is aria-hidden while it is hidden and readable once shown. Keyboard: the element takes focus when it holds nothing focusable, and focus, Enter or Space show the second child. Touch: a tap swaps, the next tap swaps back. Reduced motion swaps at once, with no wave. Only transform, opacity and paint change, so nothing around it moves. Frames run through the shared loop, which stops off screen, in hidden tabs and under reduced motion; reduced motion shows the final state with no movement.
Size
pixel-wipe.js is 1.7 KB gzip. A page loads the loader (2.9 KB) and core.js (1.4 KB) once, whatever it uses.