Docs

Magnet Lines

Magnetic Lines visually represent magnetic fields and move toward the cursor.

Run the following command

It will create a new file magnet-lines.tsx inside the components/mage-ui/background directory.

mkdir -p components/mage-ui/background && touch components/mage-ui/background/magnet-lines.tsx

Paste the code

Open the newly created file and paste the following code:

'use client';
 
import React, { useRef, useEffect, CSSProperties } from "react";
 
interface MagnetLinesProps {
  rows?: number;
  columns?: number;
  containerSize?: string;
  lineColor?: string;
  lineWidth?: string;
  lineHeight?: string;
  baseAngle?: number;
  className?: string;
  style?: CSSProperties;
}
 
const MagnetLines: React.FC<MagnetLinesProps> = ({
  rows = 9,
  columns = 9,
  containerSize = "80vmin",
  lineColor = "#efefef",
  lineWidth = "1vmin",
  lineHeight = "6vmin",
  baseAngle = -10,
  className = "",
  style = {},
}) => {
  const containerRef = useRef<HTMLDivElement | null>(null);
 
  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;
 
    const items = container.querySelectorAll<HTMLSpanElement>("span");
 
    const onPointerMove = (pointer: { x: number; y: number }) => {
      items.forEach((item) => {
        const rect = item.getBoundingClientRect();
        const centerX = rect.x + rect.width / 2;
        const centerY = rect.y + rect.height / 2;
        const b = pointer.x - centerX;
        const a = pointer.y - centerY;
        const c = Math.sqrt(a * a + b * b) || 1;
        const r =
          ((Math.acos(b / c) * 180) / Math.PI) * (pointer.y > centerY ? 1 : -1);
        item.style.setProperty("--rotate", `${r}deg`);
      });
    };
 
    const handlePointerMove = (e: PointerEvent) => {
      onPointerMove({ x: e.x, y: e.y });
    };
 
    window.addEventListener("pointermove", handlePointerMove);
 
    if (items.length) {
      const middleIndex = Math.floor(items.length / 2);
      const rect = items[middleIndex].getBoundingClientRect();
      onPointerMove({ x: rect.x, y: rect.y });
    }
 
    return () => {
      window.removeEventListener("pointermove", handlePointerMove);
    };
  }, []);
 
  const total = rows * columns;
  const spans = Array.from({ length: total }, (_, i) => (
    <span
      key={i}
      className="block origin-center"
      style={{
        backgroundColor: lineColor,
        width: lineWidth,
        height: lineHeight,
        //@ts-ignore
        "--rotate": `${baseAngle}deg`,
        transform: "rotate(var(--rotate))",
        willChange: "transform",
      }}
    />
  ));
 
  return (
    <div
      ref={containerRef}
      className={`grid place-items-center ${className}`}
      style={{
        gridTemplateColumns: `repeat(${columns}, 1fr)`,
        gridTemplateRows: `repeat(${rows}, 1fr)`,
        width: containerSize,
        height: containerSize,
        ...style,
      }}
    >
      {spans}
    </div>
  );
};
 
// Main Page Component
export default function MagnetLinesPage() {
  return (
    <div className="w-screen h-screen flex items-start justify-center">
      <MagnetLines
        rows={9}
        columns={9}
        containerSize="80vh"
        lineColor="#f0982c"
        lineWidth="1.2vmin"
        lineHeight="7.5vmin"
        baseAngle={0}
      />
    </div>
  );
}