react-digital-rain
v10.0.10
Published
This component renders beautiful neon digital rain on a black background. It fits its container. OnClick toggling into and out of fullscreen can be enabled.
Downloads
155
Maintainers
Readme
react-digital-rain 💊 🔴 🔵 ⚡ 🟢 🌧️
➡️ npmjs
This component renders neon digital rain on a black background. It fits its container. It can be enabled to go fullscreen when clicked on. This is probably best used as a screensaver, in fullscreen mode.
npm install react-digital-rain
This component uses one gif to fit any screen with no loss of resolution or stretching.
Problem:
/* styles.css */
body {
background-image: url("/images/digital_rain.gif");
background-size: cover;
background-repeat: no-repeat;
background-attachment: fixed;
}
This results in stretching for most screens and a blurry experience.
Solution:
This component uses a single gif, appending it over and over to fill the screen and timing it so that the rain looks continuous. This is possible with rain.
Positioning: We use one outer div and one inner. The inner is going to calculate the gifs for a little larger than the height/width of the container. The outer is the dimensions of the container. Turns out the browser has eccentricities when it comes to gifs. We will talk about that later. Gifs are positioned statically in columns and rows. Each row gets a 2450ms delay, which is the speed of the rain over 400 css pixels. The animation travels downward at roughly 166 css pixels per second. This achieves a seemless transition from tile to tile that fits on all screen sizes.
Browser Eccentricities - Caching: A word on caching and timing - A 500x400 gif "tile" is served to the browser with the <img> tag. If we simply use the <img> tag with src, all instances are given the same gif start time by the browser. We cannot start sequentially, even if they are appended to the DOM at a later point in time. We can break this behavior by adding a random query string to the end of the <img src> attribute. The downside is that this breaks the native browser caching and forces the browser to request the <img> on each render. We now have timing but are left with multiple expensive and slow operations. The solution is the blob (binary large object). With the blob we have control over <img> timing AND we have control over caching. The blob is our manual override to cache this gif ourselves.
Browser Eccentricities - gifs: the browser pauses gifs when they are out of view to conserve computing resources. When switching between tabs, resizing the window or resizing the parent element container, this component will simply restart the animation to regain synchronicity within the browser.
fullScreen?: boolean // enters fullscreen when clicked. Defaults to false.
animationSeconds?: number // the animation duration in seconds. If not provided, the animation duration will be calculated based on screen height
<DigitalRain />
//no animation and fullscreen
<DigitalRain fullScreen animationSeconds={0} />
//static import is discouraged (8mb will be included in the main bundle)
import DigitalRain from "react-digital-rain";
//code splitting is encouraged
const DigitalRain = React.lazy(() => import("react-digital-rain"));
const App = (props) => {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<DigitalRain />
</React.Suspense>
);
};
export default App;
//For added control of full screen feature
//I've exposed a hook called useFullScreen to know when to show/hide stuff,
//such a hiding a navbar when fullscreen.
/**
* copy/paste or use similar
* the following is a HOC to dynamically load everyting in one component
* this is to satisfy the React rule of hooks
* hook will be present on every render
*/
const withLazy = (
WrappedComponent,
importModule,
propsExtractor,
fallback = null
) => {
return (props) => {
const [loadedModule, setLoadedModule] = React.useState(null);
React.useEffect(() => {
importModule().then((module) => setLoadedModule(propsExtractor(module)));
}, []);
return loadedModule ? (
<WrappedComponent {...loadedModule} {...props} />
) : (
fallback
);
};
};
const LazyDigitalRain = withLazy(
(props) => {
const { DigitalRain, useFullScreen, setShow, ...rest } = props;
const { isFullScreen, screenfull } = useFullScreen();
React.useEffect(() => {
setShow(!isFullScreen);
}, [isFullScreen]);
return <DigitalRain {...rest} />;
},
() => import("react-digital-rain"),
(module) => ({
DigitalRain: module.DigitalRain, //DigitalRain is both named and default, FYI
useFullScreen: module.useFullScreen,
}),
<div>...Loading</div>
);
import NavBar from "@someNavBarPackage";
const App = () => {
const [show, setShow] = React.UseState(true);
return (
<>
{show && <NavBar />}
<LazyDigitalRain fullScreen setShow={setShow} />
</>
);
};
export default App;