react-particle-morpheus
v1.0.3
Published
React component to shatter, vaporize, or dissolve children elements into particles.
Maintainers
Readme
react-particle-morpheus
⚠️ This documentation was generated by AI. A proper hand-written version will be coming in the future. For now, it should serve as a solid starting point.
A React component library for shattering, vaporizing, and dissolving any children element into particles. Supports multiple animation strategies, mask generators, and physics effects — all rendered on a single global <canvas> with full control via ref.
Installation
npm install react-particle-morpheusPeer dependencies: react >= 18, react-dom >= 18
Quick Start
import { ParticlesProvider, Vaporize, type VaporizeRef } from "react-particle-morpheus";
import { useRef } from "react";
function App() {
const vaporizeRef = useRef<VaporizeRef>(null);
return (
<ParticlesProvider fps={120}>
<Vaporize
ref={vaporizeRef}
timeMaskGenerator="topLeftDiagonal"
particleInitialState="explosion"
particleEffect="gravitationBottom"
config={{ maxParticles: 2000 }}
onEnd={() => console.log("Animation finished")}
>
<div style={{ padding: 40, background: "#fff", borderRadius: 16 }}>
<h1>Click the button to dissolve me!</h1>
</div>
</Vaporize>
<button onClick={() => vaporizeRef.current?.start()}>
Start
</button>
</ParticlesProvider>
);
}Initialization — <ParticlesProvider>
ParticlesProvider is a required context provider that manages the global <canvas> and the requestAnimationFrame animation loop. It must wrap all <Vaporize> / <VaporizeDev> components.
import { ParticlesProvider } from "react-particle-morpheus";
<ParticlesProvider fps={120}>
{/* Any Vaporize components go here */}
</ParticlesProvider>Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| fps | number | ✅ | Target frames per second for the animation loop. Typical values: 60 or 120. |
| children | React.ReactNode | ✅ | Any React children — including multiple <Vaporize> instances. |
What does the Provider do?
- Mounts a single
<canvas>withposition: fixed,z-index: 9999, andpointer-events: none— covering the entire viewport. - Runs a single RAF (requestAnimationFrame) loop that iterates over all registered particle groups.
- Automatically removes a group when all of its particles have died.
- Handles window
resize(debounced at 100 ms).
useParticles() Hook
Used internally by <Vaporize>. Provides access to methods: addGroup, removeGroup, startGroup, stopGroup, detachGroup, updateGroupSource, and the current fps. Throws an error if used outside of <ParticlesProvider>.
<Vaporize> Component
The main component of the library. Wraps any React element, takes a snapshot of it (via html-to-image), generates particles in a Web Worker, and registers the group with the Provider. The entire animation is controlled via ref.
import { Vaporize, type VaporizeRef } from "react-particle-morpheus";
const ref = useRef<VaporizeRef>(null);
<Vaporize
ref={ref}
timeMaskGenerator="centerOut"
particleInitialState="explosion"
particleEffect="gravitationBottom"
config={{ maxParticles: 3000, showLogs: true }}
onStart={() => console.log("Start!")}
onShatterFinished={() => console.log("Element fully shattered")}
onEnd={() => console.log("All particles dead")}
onReset={() => console.log("Reset")}
>
<MyComponent />
</Vaporize>Props
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| children | React.ReactNode | ✅ | — | The element to vaporize. |
| ref | React.Ref<VaporizeRef> | ❌ | — | Ref providing access to control methods. |
| timeMaskGenerator | keyof MasksGenerators | ✅ | — | Shatter strategy (order in which pixels detach as particles). |
| particleInitialState | keyof ParticleInitialStates | ✅ | — | Initial particle state (velocity, direction). |
| particleEffect | keyof ParticleEffects | ✅ | — | Physics effect applied every frame. |
| config | Partial<VaporizeConfig> | ❌ | see below | Configuration object. |
| onStart | () => void | ❌ | noop | Called when the animation starts. |
| onShatterFinished | () => void | ❌ | noop | Called when the element is fully shattered into particles (all particles have spawned). |
| onEnd | () => void | ❌ | noop | Called when all particles have died. |
| onReset | () => void | ❌ | noop | Called after a reset. |
VaporizeConfig
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| maxParticles | number | 1000 | Maximum number of particles. More = more detailed effect, but higher CPU cost. |
| fps | number | 60 | Target FPS (independent of the Provider — controls internal setup throttling). |
| autoInitialize | boolean | true | Whether to automatically run snapshot + particle generation. If false, you must call resetAll() manually. |
| showLogs | boolean | false | Enables [Vaporize] console logs — useful for debugging. |
Ref Methods — VaporizeRef
| Method | Description |
|--------|-------------|
| start() | Starts the animation (requires isReady === true and isRunning === false). |
| stop() | Pauses the animation — particles keep their position, elapsed time is preserved. |
| reset() | Resets particles to their initial position (without re-snapshotting). |
| resetAll() | Full reset — new snapshot, worker, mask generation. |
| refreshSnapshot() | Refreshes the children snapshot without resetting particles (e.g. after content changes). |
| saveSnapshot(fileName?) | Downloads the current snapshot as a PNG file. |
| isReady() | Returns true if snapshot + particles + masks are ready. |
| isRunning() | Returns true if the animation is currently running. |
<VaporizeDev> Component
A development version of <Vaporize> with a built-in DevTools panel — a floating UI for changing parameters in real time. Props are the same as <Vaporize>, but all strategies have default values, so nothing is required (except children).
import { VaporizeDev } from "react-particle-morpheus";
<VaporizeDev
config={{ maxParticles: 5000 }}
onShatterFinished={() => setIsVisible(false)}
onReset={() => setIsVisible(true)}
>
<MyComponent />
</VaporizeDev>Differences from <Vaporize>
| | Vaporize | VaporizeDev |
|---|---|---|
| DevTools UI | ❌ | ✅ Floating panel with gear icon |
| timeMaskGenerator | required | defaults to "topLeftDiagonal" |
| particleInitialState | required | defaults to "explosion" |
| particleEffect | required | defaults to "gravitationBottom" |
| Runtime parameter changes | ❌ | ✅ Via DevTools panel |
| Production use | ✅ | ❌ Development only |
DevTools Panel
The DevTools panel renders as a floating button (⚙️) next to the component. It can be dragged around. Clicking it expands into a panel with:
- Buttons: Start, Stop, Reset, ResetAll, Refresh Snapshot, Save Snapshot
- Selects: Mask Generator, Initial State, Particle Effect
- Config: maxParticles, FPS, autoInitialize, showLogs
Changing the Mask Generator, Initial State, or maxParticles triggers a full remount of the <Vaporize> component (via key change).
Available Strategies
Mask Generators (timeMaskGenerator)
Control the order in which pixels detach from the element as particles.
| Key | Description |
|-----|-------------|
| leftToRight | Left to right |
| rightToLeft | Right to left |
| topToBottom | Top to bottom |
| bottomToTop | Bottom to top |
| topLeftDiagonal | Diagonal from top-left corner |
| topRightDiagonal | Diagonal from top-right corner |
| bottomLeftDiagonal | Diagonal from bottom-left corner |
| bottomRightDiagonal | Diagonal from bottom-right corner |
| sand | Sand-falling effect — waves from bottom, alternating left/right |
| centerOut | Radially from center outward |
| edgesIn | From edges toward center |
| splitHorizontal | Expanding from center horizontally |
| splitVertical | Expanding from center vertically |
| random | Random order |
Particle Initial States (particleInitialState)
Define the initial velocity and direction of each particle at spawn time.
| Key | Description |
|-----|-------------|
| explosion | Random direction, random force — classic explosion |
| scatter | Scatters mostly upward and to the sides |
| freefall | No initial velocity — particles start falling immediately |
| fountain | Fountain — strong upward throw |
| implosion | Reversed explosion — particles fly toward center |
| vortex | Rotational (tangential) motion |
| raindrop | Falling like raindrops |
| drift | Gentle, slow drift in a random direction |
Particle Effects (particleEffect)
Physics function called every frame for each living particle.
| Key | Description |
|-----|-------------|
| blowDown | Constant downward velocity |
| blowUp | Constant upward velocity |
| gravitationBottom | Gravitational acceleration downward |
| gravitationUp | Gravitational acceleration upward |
| wavyFloating | Sinusoidal wave motion + gentle floating |
| flickerFade | Random opacity flickering |
| windyGravity | Wind to the right + gravity downward |
| swirl | Swirling (velocity vector rotation) |
| shrinkFade | Rapid opacity fadeout |
When It Does NOT Work / Known Limitations
- Custom-styled scrollbars — if the children element has custom scrollbars (CSS
::-webkit-scrollbar) and does not use a scrollbar library (e.g.simplebar,react-custom-scrollbars), the snapshot may not capture them correctly or the effect may look unnatural. - Moving elements — the effect does not work correctly when the children element is mid-animation (CSS/JS), moving, or resizing at the time of snapshotting. The snapshot is taken at a single point in time and assumes a static element position.
- Elements with
position: fixed/stickyinside children — thehtml-to-imagesnapshot may incorrectly render the position of such elements. - Cross-origin resources — images or fonts from another domain (without CORS) will not be included in the snapshot.
- Very large elements — for elements with a large pixel area (e.g. 2000×2000+), generating masks and particles in the worker may take a noticeable amount of time. Adjust
maxParticlesaccordingly. - SSR / Server Components — the library requires
window,canvas, and Web Worker — it works exclusively on the client side. - Multiple
<ParticlesProvider>instances — there should be one Provider in the tree. Nesting multiple Providers will create multiple canvases.
Architecture (in brief)
<ParticlesProvider>mounts a global<canvas>and runs a single RAF loop.<Vaporize>on mount:- Takes a snapshot of children (
html-to-image→toCanvas) - Sends data to a Web Worker that generates particles + CSS masks
- Registers the group with the Provider (
addGroup)
- Takes a snapshot of children (
start()→ Provider setsisRunning=true, the RAF loop updates physics and draws particles.- Every frame, the Provider calls
onFrame— Vaporize synchronizes the children's CSS mask (the element gradually disappears). - When all particles have spawned →
onShatter. When all particles die →onEnd+ group auto-removal.
API Exports
// Components
export { Vaporize } from "./Vaporize";
export { VaporizeDev } from "./VaporizeDev";
export { ParticlesProvider, useParticles } from "./PariclesProvider";
// Strategies (for advanced use / creating your own)
export { MasksGenerators } from "./maskGenerators";
export { ParticleEffects } from "./particleEffects";
export { ParticleInitialStates } from "./particleInitialStates";
// Types
export type { VaporizeRef } from "./Vaporize";
export type { VaporizeConfig, Particle, ParticleEffect, ParticleInitialState, TimeMaskGenerator } from "./types";License
MIT
