AnimatedLogos.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import { AnimatePresence, motion } from 'framer-motion'
  2. import { Axiom, Datadog, Grafana, Last9, Otlp, Sentry } from 'icons'
  3. import { BracesIcon, Cloud, Server } from 'lucide-react'
  4. import { useEffect, useState } from 'react'
  5. import { cn } from 'ui'
  6. interface AnimatedLogosProps {
  7. iconSize?: number
  8. className?: string
  9. }
  10. export const AnimatedLogos = ({ iconSize = 36, className }: AnimatedLogosProps) => {
  11. const [currIndex, setCurrIndex] = useState(0)
  12. const timer = 2500
  13. const centerWrapperSize = Math.round(iconSize * 2.67)
  14. const sideWrapperSize = Math.round(iconSize * 2.22)
  15. const containerW = Math.round(iconSize * 5.33)
  16. const containerH = Math.round(iconSize * 3.56)
  17. const sideOffset = Math.round(iconSize * 2.22)
  18. const hiddenOffset = Math.round(iconSize * 3.33)
  19. const logos = [
  20. { id: 'webhook', name: 'Custom Endpoint', icon: <BracesIcon size={iconSize} /> },
  21. { id: 'otlp', name: 'OTLP', icon: <Otlp fill="currentColor" size={iconSize} /> },
  22. { id: 'datadog', name: 'Datadog', icon: <Datadog fill="currentColor" size={iconSize} /> },
  23. { id: 'loki', name: 'Loki', icon: <Grafana fill="currentColor" size={iconSize} /> },
  24. { id: 's3', name: 'Amazon S3', icon: <Cloud size={iconSize} /> },
  25. { id: 'sentry', name: 'Sentry', icon: <Sentry fill="currentColor" size={iconSize} /> },
  26. { id: 'axiom', name: 'Axiom', icon: <Axiom fill="currentColor" size={iconSize} /> },
  27. { id: 'last9', name: 'Last9', icon: <Last9 fill="currentColor" size={iconSize} /> },
  28. { id: 'syslog', name: 'Syslog', icon: <Server size={iconSize} /> },
  29. ]
  30. useEffect(() => {
  31. const interval = setInterval(() => {
  32. setCurrIndex((prev) => (prev + 1) % logos.length)
  33. }, timer)
  34. return () => clearInterval(interval)
  35. }, [logos.length])
  36. const getPreviousIndex = () => (currIndex - 1 + logos.length) % logos.length
  37. const getNextIndex = () => (currIndex + 1) % logos.length
  38. const getPosition = (index: number) => {
  39. if (index === currIndex) return 'center'
  40. if (index === getPreviousIndex()) return 'left'
  41. if (index === getNextIndex()) return 'right'
  42. return 'hidden'
  43. }
  44. const logoVariants = {
  45. hidden: {
  46. x: `calc(-50% + ${hiddenOffset}px)`,
  47. y: '-50%',
  48. scale: 0.6,
  49. opacity: 0,
  50. filter: 'blur(1px)',
  51. },
  52. right: {
  53. x: `calc(-50% + ${sideOffset}px)`,
  54. y: '-50%',
  55. scale: 0.8,
  56. opacity: 0.5,
  57. zIndex: 2,
  58. filter: 'blur(1px)',
  59. },
  60. center: {
  61. x: '-50%',
  62. y: '-50%',
  63. scale: 1,
  64. opacity: 1,
  65. zIndex: 3,
  66. filter: 'blur(0px)',
  67. },
  68. left: {
  69. x: `calc(-50% - ${sideOffset}px)`,
  70. y: '-50%',
  71. scale: 0.8,
  72. opacity: 0.5,
  73. zIndex: 2,
  74. filter: 'blur(1px)',
  75. },
  76. exit: {
  77. x: `calc(-50% - ${hiddenOffset}px)`,
  78. y: '-50%',
  79. scale: 0.6,
  80. opacity: 0,
  81. filter: 'blur(1px)',
  82. },
  83. }
  84. const visibleIndices = [getPreviousIndex(), currIndex, getNextIndex()]
  85. return (
  86. <div
  87. className={cn('relative overflow-hidden', className ?? 'mx-auto mb-8')}
  88. style={{ width: containerW, height: containerH }}
  89. >
  90. <AnimatePresence initial={false}>
  91. {logos.map((logo, index) => {
  92. if (!visibleIndices.includes(index)) return null
  93. const position = getPosition(index)
  94. const isCenter = index === currIndex
  95. const wrapperSize = isCenter ? centerWrapperSize : sideWrapperSize
  96. return (
  97. <motion.div
  98. key={logo.id}
  99. className="absolute top-1/2 left-1/2 flex items-center justify-center rounded-lg"
  100. style={{ width: wrapperSize, height: wrapperSize }}
  101. variants={logoVariants}
  102. initial="hidden"
  103. animate={position}
  104. exit="exit"
  105. transition={{ duration: 0.5, ease: 'easeInOut' }}
  106. >
  107. <span>{logo.icon}</span>
  108. </motion.div>
  109. )
  110. })}
  111. </AnimatePresence>
  112. <div className="absolute -inset-4 bg-linear-to-r from-background-surface-75 via-transparent to-background-surface-75 z-40" />
  113. </div>
  114. )
  115. }