Himesh Dua

Component Preview

0.0%
'use client';

import {useEffect, useRef} 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 Bar({height, color, width}: {height: string; color: string; width: any}) {
  return <motion.div style={{width}} className={cn(height, color)} />;
}

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) => {
        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 (
          <Bar
            key={idx}
            height="h-px"
            width={smoothWidth}
            color={isBig ? 'bg-black' : 'bg-neutral-500'}
          />
        );
      })}
    </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>
    </>
  );
}

function StrechedBars() {
  const mouseY = useMotionValue(0);
  const containerHeight = useMotionValue(0);
  const containerRef = useRef<HTMLDivElement>(null);

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

    const updateSize = () => {
      containerHeight.set(el.getBoundingClientRect().height);
    };

    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]);

  const TOTAL = 30;

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

export default StrechedBars;

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