{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BounceCards-JS-CSS", "type": "registry:block", "title": "BounceCards", "description": "Cards bounce that bounce in on mount.", "dependencies": [ "gsap" ], "files": [ { "path": "public/default/src/content/Components/BounceCards/BounceCards.jsx", "content": "import { useEffect } from 'react';\nimport { gsap } from 'gsap';\nimport './BounceCards.css';\n\nexport default function BounceCards({\n className = '',\n images = [],\n containerWidth = 400,\n containerHeight = 400,\n animationDelay = 0.5,\n animationStagger = 0.06,\n easeType = 'elastic.out(1, 0.8)',\n transformStyles = [\n 'rotate(10deg) translate(-170px)',\n 'rotate(5deg) translate(-85px)',\n 'rotate(-3deg)',\n 'rotate(-10deg) translate(85px)',\n 'rotate(2deg) translate(170px)'\n ],\n enableHover = true\n}) {\n useEffect(() => {\n gsap.fromTo(\n '.card',\n { scale: 0 },\n {\n scale: 1,\n stagger: animationStagger,\n ease: easeType,\n delay: animationDelay\n }\n );\n }, [animationStagger, easeType, animationDelay]);\n\n const getNoRotationTransform = transformStr => {\n const hasRotate = /rotate\\([\\s\\S]*?\\)/.test(transformStr);\n if (hasRotate) {\n return transformStr.replace(/rotate\\([\\s\\S]*?\\)/, 'rotate(0deg)');\n } else if (transformStr === 'none') {\n return 'rotate(0deg)';\n } else {\n return `${transformStr} rotate(0deg)`;\n }\n };\n\n const getPushedTransform = (baseTransform, offsetX) => {\n const translateRegex = /translate\\(([-0-9.]+)px\\)/;\n const match = baseTransform.match(translateRegex);\n if (match) {\n const currentX = parseFloat(match[1]);\n const newX = currentX + offsetX;\n return baseTransform.replace(translateRegex, `translate(${newX}px)`);\n } else {\n return baseTransform === 'none' ? `translate(${offsetX}px)` : `${baseTransform} translate(${offsetX}px)`;\n }\n };\n\n const pushSiblings = hoveredIdx => {\n if (!enableHover) return;\n images.forEach((_, i) => {\n gsap.killTweensOf(`.card-${i}`);\n\n const baseTransform = transformStyles[i] || 'none';\n\n if (i === hoveredIdx) {\n const noRotationTransform = getNoRotationTransform(baseTransform);\n gsap.to(`.card-${i}`, {\n transform: noRotationTransform,\n duration: 0.4,\n ease: 'back.out(1.4)',\n overwrite: 'auto'\n });\n } else {\n const offsetX = i < hoveredIdx ? -160 : 160;\n const pushedTransform = getPushedTransform(baseTransform, offsetX);\n\n const distance = Math.abs(hoveredIdx - i);\n const delay = distance * 0.05;\n\n gsap.to(`.card-${i}`, {\n transform: pushedTransform,\n duration: 0.4,\n ease: 'back.out(1.4)',\n delay,\n overwrite: 'auto'\n });\n }\n });\n };\n\n const resetSiblings = () => {\n if (!enableHover) return;\n images.forEach((_, i) => {\n gsap.killTweensOf(`.card-${i}`);\n const baseTransform = transformStyles[i] || 'none';\n gsap.to(`.card-${i}`, {\n transform: baseTransform,\n duration: 0.4,\n ease: 'back.out(1.4)',\n overwrite: 'auto'\n });\n });\n };\n\n return (\n