Rain
Simulates rainfall with a visual ripple effect on impact, creating a dynamic, realistic wet weather experience.
The World Shall Know Rain
Justice comes from vengeance, but that justice only breeds more vengeance. And the rain continues to fall, like the cycle of hatred. Embrace the endless rain, where peace has yet to find its place.
Installation
1Install dependencies
This library primarily relies on these libraries to function.
Text copiednpm i framer-motion clsx tailwind-merge
2Create a lib folder
In your app or src/app create a lib folder.
3Create a utils.ts file
In your lib folder create a utils.ts file.
4Add the function
Copy paste this function in your utils.ts.
Text copiedimport { ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }
5Copy the following code
In your Rain.tsx component
Text copied"use client"; import { cn } from "@/lib/utils"; import { motion } from "framer-motion"; export default function Rain({ zIndex = undefined, rainSpeed = 1.75, dropsNumber = 20, dropsWidth = 1.35, dropsHeight = 3.5, dropsColor = "#c7daf6", dropsRadius = 0.3, ripplesNumber = 3, ripplesSize = 10, ripplesSizeExpanded = 65, ripplesColor = "#73a1e7", }: RainProps) { const drops = new Array(dropsNumber).fill(true); const ripples = new Array(ripplesNumber).fill(true); return ( <div className="pointer-events-none absolute bottom-[2px] left-[2px] right-[2px] top-[2px] overflow-hidden"> {drops.map((_i, index) => { const rainDropPositionStartLeft = Math.floor(Math.random() * 99) + 1 + "%"; const rainDropPositionEndTop = Math.floor(Math.random() * 10) + 90 + "%"; const rainDropDuration = Math.floor(Math.random() * rainSpeed) + 1.15; const rainDropDelay = Math.floor( Math.random() * (Math.random() * 3 + 8) + 0.15, ); return ( <> <Drop key={index} zIndex={zIndex} dropsColor={dropsColor} dropsHeight={dropsHeight} dropsRadius={dropsRadius} dropsWidth={dropsWidth} rainDropDelay={rainDropDelay} rainDropDuration={rainDropDuration} rainDropPositionEndTop={rainDropPositionEndTop} rainDropPositionStartLeft={rainDropPositionStartLeft} /> {ripples.map((ripple, rippleIndex) => ( <Ripple key={rippleIndex} zIndex={zIndex} ripplesColor={ripplesColor} ripplesSize={ripplesSize} ripplesSizeExpanded={ripplesSizeExpanded} rippleIndex={rippleIndex} rainDropDelay={rainDropDelay} rainDropDuration={rainDropDuration} rainDropPositionEndTop={rainDropPositionEndTop} rainDropPositionStartLeft={rainDropPositionStartLeft} /> ))} </> ); })} </div> ); } const Drop = ({ dropsWidth, dropsRadius, zIndex, rainDropPositionStartLeft, dropsHeight, dropsColor, rainDropPositionEndTop, rainDropDuration, rainDropDelay, }: DropProps) => { return ( <motion.div style={{ width: dropsWidth, borderRadius: dropsRadius, }} initial={{ zIndex: zIndex || -20, left: rainDropPositionStartLeft, height: Math.floor(Math.random() * (4.5 - 3.5) + dropsHeight) + "rem", backgroundImage: `linear-gradient(to bottom, transparent, ${dropsColor}`, }} animate={{ top: [ Math.floor(Math.random() * 5) + 0.25 + "%", rainDropPositionEndTop, ], opacity: [0, 1, 1, 1, 0.85, 0], }} transition={{ duration: rainDropDuration, delay: rainDropDelay, repeat: Infinity, ease: "linear", }} className={cn("absolute")} /> ); }; const Ripple = ({ zIndex, rippleIndex, ripplesSize, ripplesSizeExpanded, ripplesColor, rainDropPositionEndTop, rainDropPositionStartLeft, rainDropDuration, rainDropDelay, }: RippleProps) => { return ( <motion.span initial={{ zIndex: zIndex || -19, top: rainDropPositionEndTop, left: rainDropPositionStartLeft, }} animate={{ opacity: [0, 1, 0], borderWidth: 2, borderColor: ripplesColor, width: [ripplesSize, ripplesSizeExpanded - rippleIndex * 10], height: [ripplesSize, ripplesSizeExpanded - rippleIndex * 10], }} transition={{ duration: 1.25, delay: rainDropDuration + rainDropDelay + rippleIndex * 0.15, repeat: Infinity, repeatDelay: rainDropDuration - 1.25, ease: "linear", }} className="absolute -translate-x-1/2 -translate-y-1/2 scale-y-50 rounded-full" /> ); }; type RainProps = { zIndex?: number; rainSpeed?: number; dropsNumber?: number; dropsWidth?: number; dropsHeight?: number; dropsColor?: string; dropsRadius?: number; ripplesNumber?: number; ripplesSize?: number; ripplesSizeExpanded?: number; ripplesColor?: string; }; type CommonProps = { zIndex?: number; rainDropPositionEndTop: string; rainDropPositionStartLeft: string; rainDropDuration: number; rainDropDelay: number; }; type DropProps = CommonProps & { dropsWidth: number; dropsHeight: number; dropsColor: string; dropsRadius: number; }; type RippleProps = CommonProps & { rippleIndex: number; ripplesSize: number; ripplesSizeExpanded: number; ripplesColor: string; };
6How to use
Make sure the div container of the Rain component has a relative position.
Text copied<div className="relative"> <Rain/> </div>
7Props
All the things you need to know about this component.
Prop | Type | Description |
---|---|---|
zIndex | number | Choose a custom z-index, if empty z-index will be -19. |
rainSpeed | number | The current speed of the rain, default : 1.75 |
dropsNumber | number | Number of drops in the rain. |
dropsWidth | number | Drops width, default : 1.35. |
dropsHeight | number | Drops width, default : 3.5. |
dropsColor | string | Color of the drops (hexadecimal : #fff). |
dropsRadius | number | Drops radius, default : 0.3. |
ripplesNumber | number | Number of ripples in the rain. |
ripplesSize | number | Size of the ripple. |
ripplesSizeExpanded | number | Size of the ripple when expanded. |
ripplesColor | string | Color of the ripples (hexadecimal : #fff). |
8How to custom
Example of customization.
Text copied<div className="relative"> <Rain zIndex={10} rainSpeed = {2.75} dropsNumber={15} dropsWidth = {1.35} dropsHeight = {3.5} dropsColor="#fff" dropsRadius = {0.3} ripplesNumber={4} ripplesSize={25} ripplesSizeExpanded={85} ripplesColor="#fff" /> </div>