๐Ÿš€ย ย ย ์ƒˆ๋กœ์šด ๋ธ”๋กœ๊ทธ๋กœ ์ด์ „ํ–ˆ์Šต๋‹ˆ๋‹ค.

(gsap) Card Hover

snippet: animation
2022.12.20
2๋ถ„

yarn add gsap
import { gsap } from 'gsap';
import { useCallback, useState } from 'react';

export default function Test() {
  const [cardElement, setCardElement] = useState<HTMLDivElement | null>(null);

  const setTransformVariable: React.MouseEventHandler<HTMLDivElement> = useCallback(
    (e) => {
      if (cardElement != null) {
        const bounds = cardElement.getBoundingClientRect();

        const mouseX = e.clientX;
        const mouseY = e.clientY;
        const leftX = mouseX - bounds.x;
        const topY = mouseY - bounds.y;

        const cardX = leftX - bounds.width / 2;
        const cardY = topY - bounds.height / 2;

        const cardRotateX = cardY / 100;
        const cardRotateY = (-1 * cardX) / 100;

        gsap.to(cardElement, {
          scale: 1.07,
          rotateX: cardRotateX * 5,
          rotateY: cardRotateY * 5,
        });
      }
    },
    [cardElement],
  );

  const clearTransformVariable = useCallback(() => {
    if (cardElement != null) {
      gsap.to(cardElement, {
        scale: 1,
        rotateX: 0,
        rotateY: 0,
      });
    }
  }, [cardElement]);

  return (
    <div>
      <div
        className="card-root"
        onMouseMove={setTransformVariable}
        onMouseLeave={clearTransformVariable}
      >
        <div className="card" ref={setCardElement}></div>
      </div>

      <style jsx>{`
        .card-root {
          display: flex;
          justify-content: center;
          align-items: center;
          perspective: 800px;
          padding: 50px;
        }

        .card {
          display: flex;
          flex-direction: column;

          width: 320px;
          height: 490px;
          box-shadow: 0px 24px 72px rgba(36, 42, 48, 0.3), inset 0px 0px 0px 1px rgba(0, 0, 0, 0.08);
          border-radius: 16px;

          transition: all 300ms ease-out;
        }
      `}</style>
    </div>
  );
}

์ฐธ๊ณ  ์ž๋ฃŒ