Better UIBetter UI

Floating Shapes Background

A dynamic animated background component with floating geometric shapes. Supports customizable shape count, animation duration, and easing for enhanced visual effects.

Overview

Floating Shapes Background is a visually engaging React component that creates an animated background with floating geometric shapes. It allows full customization of the number of shapes, animation duration, and easing functions, making it versatile for different UI designs.

This component is useful for adding subtle motion effects to backgrounds, enhancing user engagement while maintaining a clean aesthetic. It utilizes motion/react for smooth animations and is built with Tailwind CSS for easy styling.


Preview

Usage

'use client'
 
import { FloatingShapesBackground } from '@/components/background/floating-shapes-background'
 
<div className="relative h-[496px] w-full overflow-hidden rounded-lg border bg-background">
  <FloatingShapesBackground />
</div>

Code

'use client'
 
import { motion } from 'motion/react'
 
import { cn } from '@/lib/utils'
 
interface FloatingShapesBackgroundProps {
  numShapes?: number
  duration?: number
  easing?: 'linear' | 'easeIn' | 'easeOut' | 'easeInOut'
  className?: string
}
 
interface Shape {
  id: string
  type: 'circle' | 'square' | 'triangle'
  size: number
  x: number
  y: number
  rotation: number
}
 
const shapeStyles: Record<Shape['type'], string> = {
  circle: 'rounded-full bg-white/10',
  square: 'rotate-45 bg-white/10',
  triangle: 'h-0 w-0 border-solid border-transparent border-b-white opacity-10',
}
 
export const FloatingShapesBackground = ({
  numShapes = 20,
  duration = 10,
  easing = 'easeInOut',
  className,
}: FloatingShapesBackgroundProps) => {
  const getRandomShape = () => {
    return ['circle', 'square', 'triangle'][
      Math.floor(Math.random() * 3)
    ] as Shape['type']
  }
 
  const shapes = Array.from({ length: numShapes }).map(() => ({
    id: crypto.randomUUID(),
    type: getRandomShape(),
    size: Math.random() * 60 + 20,
    x: Math.random() * 100,
    y: Math.random() * 100,
    rotation: Math.random() * 360,
  }))
 
  const getShapeSize = (shape: Shape) => {
    const baseStyle = { left: `${shape.x}%`, top: `${shape.y}%` }
 
    if (shape.type === 'triangle') {
      return {
        ...baseStyle,
        borderWidth: `${shape.size / 2}px`,
        borderBottomWidth: `${shape.size}px`,
      }
    }
    return {
      ...baseStyle,
      width: shape.size,
      height: shape.size,
    }
  }
 
  return (
    <div
      className={cn(
        'absolute inset-0 overflow-hidden bg-indigo-900',
        className
      )}
    >
      {shapes.map((shape) => (
        <motion.div
          key={shape.id}
          className={cn(
            'absolute will-change-transform',
            shapeStyles[shape.type]
          )}
          style={getShapeSize(shape)}
          animate={{
            rotate: [shape.rotation, shape.rotation + 360],
            y: [`${shape.y}%`, `${shape.y - 20}%`, `${shape.y}%`],
          }}
          transition={{
            duration: duration + Math.random() * 5,
            repeat: Infinity,
            ease: easing,
          }}
        />
      ))}
    </div>
  )
}
 

Props

PropTypeDefault
numShapes
number
20
duration
number
10
easing
"linear" | "easeIn" | "easeOut" | "easeInOut"
'easeInOut'
className
string
-

On this page