UNPKG

reactbits-mcp-server

Version:

MCP Server for React Bits - Access 99+ React components with animations, backgrounds, and UI elements

165 lines (149 loc) 6 kB
import { useEffect, useState } from "react"; import { motion, useMotionValue, useAnimation, useTransform, } from "framer-motion"; const IMGS = [ "https://images.unsplash.com/photo-1528181304800-259b08848526?q=80&w=3870&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", "https://images.unsplash.com/photo-1506665531195-3566af2b4dfa?q=80&w=3870&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", "https://images.unsplash.com/photo-1520250497591-112f2f40a3f4?q=80&w=3456&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", "https://images.unsplash.com/photo-1495103033382-fe343886b671?q=80&w=3870&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", "https://images.unsplash.com/photo-1506781961370-37a89d6b3095?q=80&w=3264&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", "https://images.unsplash.com/photo-1599576838688-8a6c11263108?q=80&w=3870&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", "https://images.unsplash.com/photo-1494094892896-7f14a4433b7a?q=80&w=3870&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", "https://plus.unsplash.com/premium_photo-1664910706524-e783eed89e71?q=80&w=3869&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", "https://images.unsplash.com/photo-1503788311183-fa3bf9c4bc32?q=80&w=3870&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", "https://images.unsplash.com/photo-1585970480901-90d6bb2a48b5?q=80&w=3774&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", ]; const RollingGallery = ({ autoplay = false, pauseOnHover = false, images = [], }) => { images = images.length > 0 ? images : IMGS; const [isScreenSizeSm, setIsScreenSizeSm] = useState( window.innerWidth <= 640 ); useEffect(() => { const handleResize = () => setIsScreenSizeSm(window.innerWidth <= 640); window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); const cylinderWidth = isScreenSizeSm ? 1100 : 1800; const faceCount = images.length; const faceWidth = (cylinderWidth / faceCount) * 1.5; const radius = cylinderWidth / (2 * Math.PI); const dragFactor = 0.05; const rotation = useMotionValue(0); const controls = useAnimation(); const transform = useTransform( rotation, (val) => `rotate3d(0,1,0,${val}deg)` ); const startInfiniteSpin = (startAngle) => { controls.start({ rotateY: [startAngle, startAngle - 360], transition: { duration: 20, ease: "linear", repeat: Infinity, }, }); }; useEffect(() => { if (autoplay) { const currentAngle = rotation.get(); startInfiniteSpin(currentAngle); } else { controls.stop(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [autoplay]); const handleUpdate = (latest) => { if (typeof latest.rotateY === "number") { rotation.set(latest.rotateY); } }; const handleDrag = (_, info) => { controls.stop(); rotation.set(rotation.get() + info.offset.x * dragFactor); }; const handleDragEnd = (_, info) => { const finalAngle = rotation.get() + info.velocity.x * dragFactor; rotation.set(finalAngle); if (autoplay) { startInfiniteSpin(finalAngle); } }; const handleMouseEnter = () => { if (autoplay && pauseOnHover) { controls.stop(); } }; const handleMouseLeave = () => { if (autoplay && pauseOnHover) { const currentAngle = rotation.get(); startInfiniteSpin(currentAngle); } }; return ( <div className="relative h-[500px] w-full overflow-hidden"> <div className="absolute top-0 left-0 h-full w-[48px] z-10" style={{ background: "linear-gradient(to left, rgba(0,0,0,0) 0%, #060010 100%)", }} /> <div className="absolute top-0 right-0 h-full w-[48px] z-10" style={{ background: "linear-gradient(to right, rgba(0,0,0,0) 0%, #060010 100%)", }} /> <div className="flex h-full items-center justify-center [perspective:1000px] [transform-style:preserve-3d]"> <motion.div drag="x" dragElastic={0} onDrag={handleDrag} onDragEnd={handleDragEnd} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} animate={controls} onUpdate={handleUpdate} style={{ transform: transform, rotateY: rotation, width: cylinderWidth, transformStyle: "preserve-3d", }} className="flex min-h-[200px] cursor-grab items-center justify-center [transform-style:preserve-3d]" > {images.map((url, i) => ( <div key={i} className="group absolute flex h-fit items-center justify-center p-[8%] [backface-visibility:hidden] md:p-[6%]" style={{ width: `${faceWidth}px`, transform: `rotateY(${(360 / faceCount) * i }deg) translateZ(${radius}px)`, }} > <img src={url} alt="gallery" className="pointer-events-none h-[120px] w-[300px] rounded-[15px] border-[3px] border-white object-cover transition-transform duration-300 ease-out group-hover:scale-105 sm:h-[100px] sm:w-[220px]" /> </div> ))} </motion.div> </div> </div> ); }; export default RollingGallery;