Himesh Dua

Component Preview

0.0%
'use client';

import {useEffect, useRef, useState} from 'react';
import {motion, useMotionValue, useSpring, useTransform, type MotionValue} from 'framer-motion';
import {cn} from '@/lib/utils';

const SPRING_CONFIG = {
  stiffness: 180,
  damping: 40,
};
function BarItem({idx, TOTAL, mouseY}: {idx: number; TOTAL: number; mouseY: MotionValue<number>}) {
  const isBig = idx % 5 === 0;
  const barPosition = idx / TOTAL;

  const distance = useTransform(mouseY, y => Math.abs(y - barPosition));

  const width = useTransform(distance, d => {
    const max = isBig ? 120 : 80;
    const min = isBig ? 40 : 20;
    const spread = 0.35;
    const normalized = Math.min(d / spread, 1);
    const eased = Math.pow(1 - normalized, 2.5);

    return min + (max - min) * eased;
  });

  const smoothWidth = useSpring(width, SPRING_CONFIG);

  return (
    <motion.div
      className={cn('h-px', isBig ? 'bg-black' : 'bg-neutral-500')}
      style={{width: smoothWidth}}
    />
  );
}

export function Bars({mouseY, TOTAL}: {mouseY: MotionValue<number>; TOTAL: number}) {
  return (
    <div
      className="absolute inset-0 grid"
      style={{
        gridTemplateRows: `repeat(${TOTAL}, 1fr)`,
      }}
    >
      {Array.from({length: TOTAL}).map((_, idx) => (
        <BarItem key={idx} idx={idx} TOTAL={TOTAL} mouseY={mouseY} />
      ))}
    </div>
  );
}

function CursorLine({
  mouseY,
  containerHeight,
}: {
  mouseY: MotionValue<number>;
  containerHeight: MotionValue<number>;
}) {
  const yPx = useTransform([mouseY, containerHeight], ([y, h]: any) => y * h);

  const smoothY = useSpring(yPx, {
    stiffness: 200,
    damping: 30,
  });

  const percent = useTransform(mouseY, y => `${(y * 100).toFixed(1)}%`);

  return (
    <>
      <motion.div
        style={{y: smoothY}}
        className="absolute left-0 w-full h-px bg-red-500 pointer-events-none"
      />

      <motion.div
        style={{y: smoothY}}
        className="absolute right-4 top-2 -translate-y-1/2 text-[10px] font-mono text-red-400 pointer-events-none"
      >
        <motion.span>{percent}</motion.span>
      </motion.div>
    </>
  );
}

export function StrechedBars() {
  const mouseY = useMotionValue(0);
  const containerHeight = useMotionValue(0);
  const containerRef = useRef<HTMLDivElement>(null);
  const [total, setTotal] = useState(15);

  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;

    const updateSize = () => {
      containerHeight.set(el.getBoundingClientRect().height);
      setTotal(window.innerWidth >= 640 ? 30 : 15);
    };

    updateSize();
    window.addEventListener('resize', updateSize);

    const handleMouseMove = (e: MouseEvent) => {
      const rect = el.getBoundingClientRect();
      const y = (e.clientY - rect.top) / rect.height;
      mouseY.set(Math.max(0, Math.min(1, y)));
    };

    el.addEventListener('mousemove', handleMouseMove);

    return () => {
      window.removeEventListener('resize', updateSize);
      el.removeEventListener('mousemove', handleMouseMove);
    };
  }, [mouseY, containerHeight]);

  return (
    <div ref={containerRef} className="relative size-full">
      <Bars mouseY={mouseY} TOTAL={total} />
      <CursorLine mouseY={mouseY} containerHeight={containerHeight} />
    </div>
  );
}

Streched Bars

An interactive bar field that dynamically responds to cursor movement, using distance-based interpolation and spring physics to create a fluid, wave-like motion effect.

Framer MotionInteractive UIMotion ValuesSpring AnimationCursor Tracking