react-dvd-screensaver
v0.1.2
Published
DVD-era nostalgia in React. Zero dependencies, fully typed, SSR-safe, supports React 16 through 19.
Maintainers
Readme
react-dvd-screensaver
DVD-era nostalgia in React. Zero dependencies, fully typed, works on desktop and mobile, supports React 16 through 19.
pnpm add react-dvd-screensaver
# or
npm i react-dvd-screensaverUsage
import { useDvdScreensaver } from 'react-dvd-screensaver';
function MyComponent() {
const { containerRef, elementRef, impactCount } = useDvdScreensaver({ speed: 3 });
return (
<div ref={containerRef} style={{ position: 'relative', width: '100%', height: '400px' }}>
<div ref={elementRef} style={{ position: 'absolute', top: 0, left: 0 }}>
<MyLogo />
</div>
</div>
);
}The container element needs position: relative (or absolute / fixed) to establish a positioning context. The animated element needs position: absolute; top: 0; left: 0 as its starting point.
Options
| Option | Type | Default | Description |
|---|---|---|---|
| speed | number | 2 | Speed of the animation in pixels per frame |
| freezeOnHover | boolean | false | Pause animation on mouse hover or touch |
| paused | boolean | false | Programmatically pause and resume the animation |
| impactCallback | (count: number) => void | — | Called on each boundary impact with total count |
| onCornerHit | () => void | — | Called when the element hits a corner (both axes at once) |
| hoverCallback | () => void | — | Called when hover or touch begins |
Return values
| Property | Type | Description |
|---|---|---|
| containerRef | RefObject | Attach to the container element |
| elementRef | RefObject | Attach to the element that should animate |
| hovered | boolean | Whether the element is currently hovered or touched |
| impactCount | number | Total number of boundary impacts |
Examples
Change color on impact
const COLORS = ['#ff4081', '#7c4dff', '#00bcd4', '#69f0ae'];
function MyComponent() {
const [color, setColor] = useState(COLORS[0]);
const { containerRef, elementRef } = useDvdScreensaver({
impactCallback: (count) => setColor(COLORS[count % COLORS.length]),
});
return (
<div ref={containerRef} style={{ position: 'relative', width: '100%', height: '400px' }}>
<div ref={elementRef} style={{ position: 'absolute', top: 0, left: 0 }}>
<MyLogo fill={color} />
</div>
</div>
);
}Pause and resume
function MyComponent() {
const [paused, setPaused] = useState(false);
const { containerRef, elementRef } = useDvdScreensaver({ paused });
return (
<>
<div ref={containerRef} style={{ position: 'relative', width: '100%', height: '400px' }}>
<div ref={elementRef} style={{ position: 'absolute', top: 0, left: 0 }}>
<MyLogo />
</div>
</div>
<button onClick={() => setPaused((p) => !p)}>{paused ? 'Resume' : 'Pause'}</button>
</>
);
}Corner hit detection
function MyComponent() {
const { containerRef, elementRef } = useDvdScreensaver({
onCornerHit: () => console.log('Corner!'),
});
// ...
}Next.js App Router
Add the 'use client' directive. The hook is SSR-safe and will not cause hydration mismatches.
'use client';
import { useDvdScreensaver } from 'react-dvd-screensaver';
export default function ScreensaverPage() {
const { containerRef, elementRef } = useDvdScreensaver({ speed: 3 });
return (
<div ref={containerRef} style={{ position: 'relative', width: '100%', height: '100vh' }}>
<div ref={elementRef} style={{ position: 'absolute', top: 0, left: 0 }}>
<MyLogo />
</div>
</div>
);
}