UNPKG

reactbits-mcp-server

Version:

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

147 lines (132 loc) 3.7 kB
import { useRef, useLayoutEffect, useState } from "react"; import { motion, useScroll, useSpring, useTransform, useMotionValue, useVelocity, useAnimationFrame, } from "framer-motion"; import "./ScrollVelocity.css"; function useElementWidth(ref) { const [width, setWidth] = useState(0); useLayoutEffect(() => { function updateWidth() { if (ref.current) { setWidth(ref.current.offsetWidth); } } updateWidth(); window.addEventListener("resize", updateWidth); return () => window.removeEventListener("resize", updateWidth); }, [ref]); return width; } export const ScrollVelocity = ({ scrollContainerRef, texts = [], velocity = 100, className = "", damping = 50, stiffness = 400, numCopies = 6, velocityMapping = { input: [0, 1000], output: [0, 5] }, parallaxClassName = "parallax", scrollerClassName = "scroller", parallaxStyle, scrollerStyle, }) => { function VelocityText({ children, baseVelocity = velocity, scrollContainerRef, className = "", damping, stiffness, numCopies, velocityMapping, parallaxClassName, scrollerClassName, parallaxStyle, scrollerStyle, }) { const baseX = useMotionValue(0); const scrollOptions = scrollContainerRef ? { container: scrollContainerRef } : {}; const { scrollY } = useScroll(scrollOptions); const scrollVelocity = useVelocity(scrollY); const smoothVelocity = useSpring(scrollVelocity, { damping: damping ?? 50, stiffness: stiffness ?? 400, }); const velocityFactor = useTransform( smoothVelocity, velocityMapping?.input || [0, 1000], velocityMapping?.output || [0, 5], { clamp: false } ); const copyRef = useRef(null); const copyWidth = useElementWidth(copyRef); function wrap(min, max, v) { const range = max - min; const mod = (((v - min) % range) + range) % range; return mod + min; } const x = useTransform(baseX, (v) => { if (copyWidth === 0) return "0px"; return `${wrap(-copyWidth, 0, v)}px`; }); const directionFactor = useRef(1); useAnimationFrame((t, delta) => { let moveBy = directionFactor.current * baseVelocity * (delta / 1000); if (velocityFactor.get() < 0) { directionFactor.current = -1; } else if (velocityFactor.get() > 0) { directionFactor.current = 1; } moveBy += directionFactor.current * moveBy * velocityFactor.get(); baseX.set(baseX.get() + moveBy); }); const spans = []; for (let i = 0; i < numCopies; i++) { spans.push( <span className={className} key={i} ref={i === 0 ? copyRef : null}> {children} </span> ); } return ( <div className={parallaxClassName} style={parallaxStyle}> <motion.div className={scrollerClassName} style={{ x, ...scrollerStyle }} > {spans} </motion.div> </div> ); } return ( <section> {texts.map((text, index) => ( <VelocityText key={index} className={className} baseVelocity={index % 2 !== 0 ? -velocity : velocity} scrollContainerRef={scrollContainerRef} damping={damping} stiffness={stiffness} numCopies={numCopies} velocityMapping={velocityMapping} parallaxClassName={parallaxClassName} scrollerClassName={scrollerClassName} parallaxStyle={parallaxStyle} scrollerStyle={scrollerStyle} > {text}&nbsp; </VelocityText> ))} </section> ); }; export default ScrollVelocity;