Skip to content
Docs
Orbiting Items

Orbiting Items

List component with orbiting items. The items orbit around the center of the list.

Loading...

Installation

CLI

pnpm dlx shadcn@latest add https://animata.design/r/list/orbiting-items.json

Manual

Add to your CSS

@keyframes rotate-full {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

Run the following command

It will create a new file orbiting-items.tsx inside the components/animata/list directory.

mkdir -p components/animata/list && touch components/animata/list/orbiting-items.tsx

Paste the code

Open the newly created file and paste the following code:

import { Icons } from "@/components/icons";
import { cn } from "@/lib/utils";
 
export const testOrbitingItems = [
  <Icons.gitHub key="github" className="h-6 w-6 text-black" />,
  <Icons.twitter key="twitter" className="h-6 w-6 text-black" />,
  <Icons.react key="yarn" className="h-6 w-6 text-black" />,
  <Icons.tailwind key="tailwind" className="h-6 w-6 text-black" />,
  <Icons.framerMotion key="framer" className="h-6 w-6 text-black" />,
  <Icons.apple key="apple" className="h-6 w-6 text-black" />,
];
 
interface OrbitingItemsProps {
  /**
   * The radius of the circle in percentage, relative to the container.
   */
  radius: number;
 
  /**
   * The items to orbit around the center of the parent element.
   */
  items: React.ReactNode[];
 
  /**
   * Pause the animation when the parent element is hovered.
   */
  pauseOnHover?: boolean;
 
  /**
   * Class name for the background element.
   */
  backgroundClassName?: string;
 
  /**
   * Class name for the container element.
   */
  containerClassName?: string;
 
  /**
   * Additional classes for the item container.
   */
  className?: string;
}
 
const calculateItemStyle = ({
  index,
  radius,
  totalItems,
}: {
  radius: number;
  index: number;
  totalItems: number;
}) => {
  const angle = (index / totalItems) * 360;
  const radians = (angle * Math.PI) / 180;
  const x = radius * Math.cos(radians);
  const y = radius * Math.sin(radians);
  return {
    left: `${50 + x}%`,
    top: `${50 + y}%`,
    transform: "translate(-50%, -50%)",
  };
};
 
export default function OrbitingItems({
  radius = 50,
  items = testOrbitingItems,
  pauseOnHover,
  backgroundClassName,
  containerClassName,
  className,
}: OrbitingItemsProps) {
  // The idea is to distribute the items in a circle around the center of the parent element.
  // Then the whole parent element rotates around its center.
  // The items rotate in the opposite direction to the parent element so they appear to be stationary.
 
  const reverse = cn(
    "animate-rotate-full transition-transform ease-linear direction-reverse repeat-infinite",
    {
      "group-hover/orbit:[animation-play-state:paused]": pauseOnHover,
    },
  );
 
  return (
    <div
      className={cn(
        "full-content group/orbit flex items-center justify-center py-32",
        containerClassName,
      )}
    >
      <div
        className={cn(
          "absolute inset-0 h-full w-full items-center [background:radial-gradient(125%_125%_at_50%_10%,#030637_30%,#10439F_100%)]",
          backgroundClassName,
        )}
      />
      <div
        className={cn(
          "relative flex h-64 w-64 animate-rotate-full items-center justify-center ease-linear repeat-infinite",
          {
            "group-hover/orbit:[animation-play-state:paused]": pauseOnHover,
          },
          className,
        )}
      >
        <div className="absolute h-full w-full rounded-full border-2 border-gray-500" />
        {items.map((item, index) => {
          return (
            <div
              key={index}
              className="absolute flex h-12 w-12 items-center justify-center rounded-full bg-gray-200"
              style={calculateItemStyle({
                index,
                radius,
                totalItems: items.length,
              })}
            >
              <div className={reverse}>{item}</div>
            </div>
          );
        })}
 
        <div
          className={cn("absolute h-1/2 w-1/2 rounded-full border-2 border-gray-700", reverse)}
        />
      </div>
    </div>
  );
}

Credits

Built by hari