npm.io
0.1.2 • Published 4 months ago

react-dvd-screensaver

Licence
MIT
Version
0.1.2
Deps
0
Size
23 kB
Vulns
0
Weekly
0
Stars
20

react-dvd-screensaver

TypeScript npm version bundle size

DVD-era nostalgia in React. Zero dependencies, fully typed, works on desktop and mobile, supports React 16 through 19.

Demo · Try on StackBlitz


pnpm add react-dvd-screensaver
# or
npm i react-dvd-screensaver

Usage

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>
  );
}

License

MIT

Keywords