Search Something...

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 copied
npm 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 copied
import { 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.

PropTypeDescription
zIndexnumberChoose a custom z-index, if empty z-index will be -19.
rainSpeednumberThe current speed of the rain, default : 1.75
dropsNumbernumberNumber of drops in the rain.
dropsWidthnumberDrops width, default : 1.35.
dropsHeightnumberDrops width, default : 3.5.
dropsColorstringColor of the drops (hexadecimal : #fff).
dropsRadiusnumberDrops radius, default : 0.3.
ripplesNumbernumberNumber of ripples in the rain.
ripplesSizenumberSize of the ripple.
ripplesSizeExpandednumberSize of the ripple when expanded.
ripplesColorstringColor 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>