Docs
Animated Network
Animated Network
An interactive animated network background with responsive dots and connections
Animated Network
Expecto UI-tronum!
Summon the guardian of sleek interfaces
Installation
Copy and paste the following code into your project.
"use client"
import React, { useEffect, useRef, useState } from "react"
import { useTheme } from "next-themes"
interface Dot {
x: number
y: number
baseX: number
baseY: number
size: number
color: string
vx: number
vy: number
}
export function AnimatedNetwork() {
const canvasRef = useRef<HTMLCanvasElement>(null)
const [dots, setDots] = useState<Dot[]>([])
const [mousePos, setMousePos] = useState<{ x: number; y: number } | null>(
null
)
const { theme } = useTheme()
const dotCount = 80
const moveRadius = 120
const connectionDistance = 150
const lightColors = ["#3498db", "#e74c3c", "#2ecc71", "#f39c12", "#9b59b6"]
const darkColors = ["#5dade2", "#ec7063", "#58d68d", "#f5b041", "#af7ac5"]
useEffect(() => {
const canvas = canvasRef.current
if (!canvas) return
const ctx = canvas.getContext("2d")
if (!ctx) return
const resizeCanvas = () => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
}
resizeCanvas()
window.addEventListener("resize", resizeCanvas)
// Initialize dots
const newDots: Dot[] = []
for (let i = 0; i < dotCount; i++) {
newDots.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
baseX: Math.random() * canvas.width,
baseY: Math.random() * canvas.height,
size: Math.random() * 2 + 1,
color: (theme === "dark" ? darkColors : lightColors)[
Math.floor(Math.random() * 5)
],
vx: (Math.random() - 0.5) * 0.5,
vy: (Math.random() - 0.5) * 0.5,
})
}
setDots(newDots)
const updateMousePosition = (e: MouseEvent) => {
const rect = canvas.getBoundingClientRect()
setMousePos({
x: e.clientX - rect.left,
y: e.clientY - rect.top,
})
}
canvas.addEventListener("mousemove", updateMousePosition)
canvas.addEventListener("mouseleave", () => setMousePos(null))
return () => {
window.removeEventListener("resize", resizeCanvas)
canvas.removeEventListener("mousemove", updateMousePosition)
canvas.removeEventListener("mouseleave", () => setMousePos(null))
}
}, [theme]) // Add theme as a dependency
useEffect(() => {
const canvas = canvasRef.current
if (!canvas) return
const ctx = canvas.getContext("2d")
if (!ctx) return
let animationFrameId: number
const animate = (time: number) => {
ctx.clearRect(0, 0, canvas.width, canvas.height)
// Set background based on theme
ctx.fillStyle = theme === "dark" ? "#000000" : "#ffffff"
ctx.fillRect(0, 0, canvas.width, canvas.height)
dots.forEach((dot, index) => {
// Move dots
dot.x += dot.vx
dot.y += dot.vy
// Bounce off edges
if (dot.x < 0 || dot.x > canvas.width) dot.vx *= -1
if (dot.y < 0 || dot.y > canvas.height) dot.vy *= -1
if (mousePos) {
const dx = mousePos.x - dot.x
const dy = mousePos.y - dot.y
const distance = Math.sqrt(dx * dx + dy * dy)
if (distance < moveRadius) {
const angle = Math.atan2(dy, dx)
const push = (1 - distance / moveRadius) * 5 // Reduced push strength
dot.x -= Math.cos(angle) * push
dot.y -= Math.sin(angle) * push
}
}
// Subtle pulsing effect
const scale = 1 + Math.sin(time * 0.003 + index) * 0.1
// Draw connections
dots.forEach((otherDot, otherIndex) => {
if (index !== otherIndex) {
const dx = dot.x - otherDot.x
const dy = dot.y - otherDot.y
const distance = Math.sqrt(dx * dx + dy * dy)
if (distance < connectionDistance) {
ctx.beginPath()
ctx.moveTo(dot.x, dot.y)
ctx.lineTo(otherDot.x, otherDot.y)
const gradient = ctx.createLinearGradient(
dot.x,
dot.y,
otherDot.x,
otherDot.y
)
gradient.addColorStop(0, dot.color)
gradient.addColorStop(1, otherDot.color)
ctx.strokeStyle = gradient
ctx.lineWidth = 0.5 * (1 - distance / connectionDistance)
ctx.stroke()
}
}
})
// Draw dot
ctx.beginPath()
ctx.arc(dot.x, dot.y, dot.size * scale, 0, Math.PI * 2)
ctx.fillStyle = dot.color
ctx.fill()
})
animationFrameId = requestAnimationFrame(animate)
}
animate(0)
return () => {
cancelAnimationFrame(animationFrameId)
}
}, [dots, mousePos, theme]) // Add theme as a dependency
return (
<canvas ref={canvasRef} className="absolute inset-0 w-full h-full z-0" />
)
}
Update the import paths to match your project setup.
Usage
import AnimatedNetwork from "@/components/ui/animatednetwork"
export default function AnimatedNetworkDemo() {
return (
<div className="relative h-screen w-full">
<AnimatedNetwork />
<div className="absolute inset-0 flex items-center justify-center z-10">
{/* Your content here */}
</div>
</div>
)
}
Features
The AnimatedNetwork
component provides the following features:
- Responsive canvas that adjusts to window size
- Interactive dots that respond to mouse movement
- Dynamic connections between dots
- Theme-aware coloring (light and dark mode support)
- Subtle pulsing effect on dots
Customization
You can customize the AnimatedNetwork
component by modifying the following variables in the component:
dotCount
: Number of dots in the networkmoveRadius
: Radius of influence for mouse interactionconnectionDistance
: Maximum distance for drawing connections between dotslightColors
anddarkColors
: Arrays of colors for light and dark themes
Notes
- The component uses the
useTheme
hook fromnext-themes
for theme awareness. Make sure to set up your theme provider accordingly. - The canvas is set to
z-0
, allowing you to place content on top of it using higher z-index values.