UNPKG

reactbits-mcp-server

Version:

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

126 lines (106 loc) 3.38 kB
import { useRef, useEffect, useState } from 'react'; import { gsap } from 'gsap'; import './PixelTransition.css'; function PixelTransition({ firstContent, secondContent, gridSize = 7, pixelColor = 'currentColor', animationStepDuration = 0.3, className = '', style = {}, aspectRatio = '100%', }) { const containerRef = useRef(null); const pixelGridRef = useRef(null); const activeRef = useRef(null); const delayedCallRef = useRef(null); const [isActive, setIsActive] = useState(false); const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.matchMedia('(pointer: coarse)').matches; useEffect(() => { const pixelGridEl = pixelGridRef.current; if (!pixelGridEl) return; pixelGridEl.innerHTML = ''; for (let row = 0; row < gridSize; row++) { for (let col = 0; col < gridSize; col++) { const pixel = document.createElement('div'); pixel.classList.add('pixelated-image-card__pixel'); pixel.style.backgroundColor = pixelColor; const size = 100 / gridSize; pixel.style.width = `${size}%`; pixel.style.height = `${size}%`; pixel.style.left = `${col * size}%`; pixel.style.top = `${row * size}%`; pixelGridEl.appendChild(pixel); } } }, [gridSize, pixelColor]); const animatePixels = (activate) => { setIsActive(activate); const pixelGridEl = pixelGridRef.current; const activeEl = activeRef.current; if (!pixelGridEl || !activeEl) return; const pixels = pixelGridEl.querySelectorAll('.pixelated-image-card__pixel'); if (!pixels.length) return; gsap.killTweensOf(pixels); if (delayedCallRef.current) { delayedCallRef.current.kill(); } gsap.set(pixels, { display: 'none' }); const totalPixels = pixels.length; const staggerDuration = animationStepDuration / totalPixels; gsap.to(pixels, { display: 'block', duration: 0, stagger: { each: staggerDuration, from: 'random' } }); delayedCallRef.current = gsap.delayedCall(animationStepDuration, () => { activeEl.style.display = activate ? 'block' : 'none'; activeEl.style.pointerEvents = activate ? 'none' : ''; }); gsap.to(pixels, { display: 'none', duration: 0, delay: animationStepDuration, stagger: { each: staggerDuration, from: 'random' } }); }; const handleMouseEnter = () => { if (!isActive) animatePixels(true); }; const handleMouseLeave = () => { if (isActive) animatePixels(false); }; const handleClick = () => { animatePixels(!isActive); }; return ( <div ref={containerRef} className={`pixelated-image-card ${className}`} style={style} onMouseEnter={!isTouchDevice ? handleMouseEnter : undefined} onMouseLeave={!isTouchDevice ? handleMouseLeave : undefined} onClick={isTouchDevice ? handleClick : undefined} > <div style={{ paddingTop: aspectRatio }} /> <div className="pixelated-image-card__default"> {firstContent} </div> <div className="pixelated-image-card__active" ref={activeRef}> {secondContent} </div> <div className="pixelated-image-card__pixels" ref={pixelGridRef} /> </div> ); } export default PixelTransition;