DEV Community

Cover image for Animated Hover Logo using Typescript, Tailwind and Framer Motion
Bro Karim
Bro Karim

Posted on

Animated Hover Logo using Typescript, Tailwind and Framer Motion

It transforms a static logo into a button, which activates a video tooltip that follows the cursor's movement along the button's axis using motion values.

Upon closer inspection, you'll notice we use micro-animations. When the logo is hovered over, it smoothly scales down and fades out, then transitions into a button that scales up with a fade-in effect. Hovering over the button then triggers a video tooltip.

Demo

Source Code

logo-hover.tsx


import { useState, useCallback } from "react";
import { motion, AnimatePresence, useMotionValue } from "framer-motion";
import { Play } from "lucide-react";

export default function LogoHover() {
  const [isHovered, setIsHovered] = useState(false);
  const [showVideo, setShowVideo] = useState(false);
  const x = useMotionValue(0);

  const handleMouseMove = useCallback(
    (event: React.MouseEvent<HTMLElement>) => {
      const halfWidth = event.currentTarget.offsetWidth / 2;
      x.set(event.nativeEvent.offsetX - halfWidth);
    },
    [x]
  );

  return (
    <div className="relative w-[100px] h-[80px] cursor-pointer" onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      <AnimatePresence>
        {!isHovered && (
          // change with your own logo
          <motion.img src="/theo-logo.png" alt="Wegic Logo" className="absolute inset-0 w-full h-full object-contain" initial={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.8 }} transition={{ duration: 0.2 }} />
        )}
      </AnimatePresence>

      <AnimatePresence>
        {isHovered && (
          <motion.div className="absolute inset-0 flex items-center justify-center w-full" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0, scale: 0.8 }} transition={{ duration: 0.2 }}>
            <button
              className="flex w-full items-center justify-center  gap-2 bg-white border border-black h-10  py-2 rounded-md"
              onMouseMove={handleMouseMove}
              onMouseEnter={() => setShowVideo(true)}
              onMouseLeave={() => setShowVideo(false)}
            >
              <Play size={16} fill="white" strokeWidth={2} aria-hidden="true" />
              <p className="text-[10px] flex font-semibold">watch video</p>
            </button>
          </motion.div>
        )}
      </AnimatePresence>
      {/* Video Tooltip */}
      <AnimatePresence mode="popLayout">
        {showVideo && (
          <motion.div
            initial={{ opacity: 0, y: 20, scale: 0.6 }}
            animate={{
              opacity: 1,
              y: 0,
              scale: 1,
              transition: {
                stiffness: 260,
                damping: 10,
                duration: 0.3,
              },
              width: "200px",
              height: "auto",
            }}
            exit={{ opacity: 0, y: 20, scale: 0.6 }}
            style={{
              translateX: x,
            }}
            className="absolute top-full mt-2 z-50"
          >
            <motion.div className="rounded-md overflow-hidden ">
              <iframe
                width="560"
                height="315"
                src="https://www.youtube.com/embed/ZK-rNEhJIDs?si=KBECTf4W-b_37Xsn&amp;autoplay=1&mute=1&controls=0"
                title="YouTube video player"
                className="w-full h-full object-cover rounded-md border-[1px] border-white ring-1 ring-black/5"
                allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
                allowFullScreen
              ></iframe>
            </motion.div>
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );
}


Enter fullscreen mode Exit fullscreen mode

Top comments (0)