Icon Cloud
magicui/icon-cloudFreeComponent
An interactive 3D tag cloud component
Live preview
live · rendered by the registry
Rendered by magicui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://magicui.design/r/icon-cloud.jsonSource
1"use client"23import React, { useEffect, useRef, useState } from "react"4import { Pause, Play } from "lucide-react"5import { renderToString } from "react-dom/server"67import { Button } from "@/components/ui/button"89interface Icon {10 x: number11 y: number12 z: number13 scale: number14 opacity: number15 id: number16}1718interface IconCloudProps {19 icons?: React.ReactNode[]20 images?: string[]21 showControl?: boolean22}2324function easeOutCubic(t: number): number {25 return 1 - Math.pow(1 - t, 3)26}2728export function IconCloud({29 icons,30 images,31 showControl = true,32}: IconCloudProps) {33 const canvasRef = useRef<HTMLCanvasElement>(null)34 const [iconPositions, setIconPositions] = useState<Icon[]>([])35 const [isDragging, setIsDragging] = useState(false)36 const [isPaused, setIsPaused] = useState(false)37 const [lastMousePos, setLastMousePos] = useState({ x: 0, y: 0 })38 const [mousePos, setMousePos] = useState({ x: 0, y: 0 })39 const [targetRotation, setTargetRotation] = useState<{40 x: number41 y: number42 startX: number43 startY: number44 distance: number45 startTime: number46 duration: number47 } | null>(null)48 const animationFrameRef = useRef<number>(0)49 const rotationRef = useRef({ x: 0, y: 0 })50 const iconCanvasesRef = useRef<HTMLCanvasElement[]>([])51 const imagesLoadedRef = useRef<boolean[]>([])5253 // Pause animation if user prefers reduced motion54 useEffect(() => {55 const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)")56 if (mediaQuery.matches) {57 setIsPaused(true)58 }5960 const handleChange = (e: MediaQueryListEvent) => {61 setIsPaused(e.matches)62 }6364 mediaQuery.addEventListener("change", handleChange)65 return () => mediaQuery.removeEventListener("change", handleChange)66 }, [])6768 // Create icon canvases once when icons/images change69 useEffect(() => {70 if (!icons && !images) return7172 const items = icons ?? images ?? []73 imagesLoadedRef.current = new Array(items.length).fill(false)7475 const newIconCanvases = items.map((item, index) => {76 const offscreen = document.createElement("canvas")77 offscreen.width = 4078 offscreen.height = 4079 const offCtx = offscreen.getContext("2d")8081 if (offCtx) {82 if (images) {83 // Handle image URLs directly84 const img = new Image()85 img.crossOrigin = "anonymous"86 img.src = items[index] as string87 img.onload = () => {88// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from magicui
All 247 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Androidandroid | magicui | Components | Free | no deps | |
| Animated Beamanimated-beam | magicui | Components | Free | 1 dep | |
| Animated Circular Progress Baranimated-circular-progress-bar | magicui | Components | Free | no deps | |
| Animated Gradient Textanimated-gradient-text | magicui | Components | Free | no deps | |
| Animated Grid Patternanimated-grid-pattern | magicui | Components | Free | 1 dep | |
| Animated Listanimated-list | magicui | Components | Free | 1 dep | |
| Animated Shiny Textanimated-shiny-text | magicui | Components | Free | no deps | |
| Theme Toggleranimated-theme-toggler | magicui | Components | Free | 1 dep |
