Wave Effect

Wave effect is a modern animation component designed to enrich user interaction by creating a dynamic ripple effect when clicking on buttons or any HTML element. This component generates a visual wave effect, making the user experience more interactive and visually appealing. Perfect for modern web designs. Built with Tailwind CSS and Framer Motion.

Button

Image

Colored

Blue
Green
Purple
Red

Install the following dependencies:

npm i framer-motion clsx tailwind-merge

Add util file

import { ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

Copy and paste the following code into your project.

"use client";

import { useEffect, useState } from "react";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";

interface WaveEffectProps {
  children: React.ReactNode;
  color?: string;
  className?: string;
  duration?: number;
}

export function WaveEffect({
  children,
  color,
  className,
  duration = 0.6,
}: WaveEffectProps) {
  const [keyValue, setKeyValue] = useState<number>(0);
  const [ripple, setRipple] = useState<{ x: number; y: number }>({
    x: 0,
    y: 0,
  });
  const [showRipple, setShowRipple] = useState<boolean>(false);
  const [mouseUp, setMouseUp] = useState<boolean>(false);
  const [animationComplete, setAnimationComplete] = useState<boolean>(false);

  useEffect(() => {
    if (!mouseUp && animationComplete) {
      setShowRipple(false);
      setAnimationComplete(false);
    }
  }, [mouseUp, animationComplete]);

  const handleMouseDown = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
    const rect = e.currentTarget.getBoundingClientRect();
    const x = e.clientX - rect.left - rect.width / 2;
    const y = e.clientY - rect.top - rect.width / 2;

    setKeyValue((prev) => prev + 1);
    setRipple({ x, y });
    setMouseUp(true);
    setShowRipple(true);
  };

  const handleAnimationComplete = () => {
    setAnimationComplete(true);
  };

  const handleMouseUp = () => {
    setMouseUp(false);
  };

  return (
    <div
      className={cn("inline-flex relative overflow-hidden", className)}
      onMouseDown={handleMouseDown}
      onMouseUp={handleMouseUp}
    >
      {children}
      {showRipple && (
        <motion.span
          key={keyValue}
          className={cn(
            "absolute aspect-square bg-black bg-opacity-30 rounded-full pointer-events-none start-0 top-0",
            color
          )}
          initial={{ opacity: 1, scale: 0.5 }}
          animate={{ opacity: 1, scale: 3.5 }}
          transition={{ duration, ease: "easeOut" }}
          onAnimationComplete={handleAnimationComplete}
          style={{
            top: ripple.y,
            left: ripple.x,
            width: "100%",
          }}
        />
      )}
    </div>
  );
}

API

PropTypeDefaultDescription
childrenReact.ReactNode-The content or elements that are wrapped by the component.
colorstring (Tailwind CSS color class)"bg-white"The color of the ripple effect. Can be a Tailwind CSS color class such as bg-red-500, bg-blue-300.
classNamestring""Custom CSS classes for further styling or overriding default styles.
durationnumber0.6The duration of the ripple animation in seconds.