Feat/BOOK-424 campaign banner
Approved-by: Bianca Widstam
This commit is contained in:
93
apps/scandic-web/components/MarqueeText/index.tsx
Normal file
93
apps/scandic-web/components/MarqueeText/index.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
"use client"
|
||||
|
||||
import { cx } from "class-variance-authority"
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
|
||||
import styles from "./marqueeText.module.css"
|
||||
|
||||
interface MarqueeTextProps
|
||||
extends React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>> {
|
||||
backgroundColor: string
|
||||
textWrapperClassName?: string
|
||||
}
|
||||
|
||||
export function MarqueeText({
|
||||
backgroundColor,
|
||||
children,
|
||||
className,
|
||||
textWrapperClassName,
|
||||
...props
|
||||
}: MarqueeTextProps) {
|
||||
const textContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [dimensions, setDimensions] = useState({
|
||||
containerWidth: 0,
|
||||
contentWidth: 0,
|
||||
isOverflowing: false,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const element = textContainerRef.current
|
||||
const parentElement = element?.parentElement
|
||||
if (!parentElement) {
|
||||
return
|
||||
}
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
const containerWidth = element.clientWidth
|
||||
const contentWidth = element.scrollWidth
|
||||
const isOverflowing = contentWidth > containerWidth
|
||||
|
||||
setDimensions({
|
||||
containerWidth,
|
||||
contentWidth,
|
||||
isOverflowing,
|
||||
})
|
||||
|
||||
if (isOverflowing && containerWidth > 0) {
|
||||
const scrollDistance = contentWidth - containerWidth
|
||||
parentElement.style.setProperty(
|
||||
"--scroll-distance",
|
||||
`${scrollDistance}px`
|
||||
)
|
||||
|
||||
// Calculate dynamic animation duration based on scroll distance
|
||||
// This is done to avoid long scrolling durations for small distances and vice versa
|
||||
// Base formula: minimum 2s, add 50ms per pixel of scroll distance
|
||||
const baseDuration = 2
|
||||
const durationPerPixel = 0.05
|
||||
const calculatedDuration = Math.max(
|
||||
baseDuration,
|
||||
baseDuration + scrollDistance * durationPerPixel
|
||||
)
|
||||
|
||||
parentElement.style.setProperty(
|
||||
"--animation-duration",
|
||||
`${calculatedDuration}s`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
resizeObserver.observe(element)
|
||||
|
||||
return () => resizeObserver.disconnect()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(styles.marqueeText, className)}
|
||||
style={
|
||||
{ "--marquee-background-color": backgroundColor } as React.CSSProperties
|
||||
}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
ref={textContainerRef}
|
||||
className={cx(styles.textWrapper, textWrapperClassName, {
|
||||
[styles.overflowing]: dimensions.isOverflowing,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user