-
-
Save dragonman225/b60261d2c9007ec5ef2a7f8a0416a25a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.marquee { | |
@apply flex-1 flex overflow-hidden; | |
} | |
.marquee .marquee-inner { | |
@apply truncate flex-1; | |
} | |
.marquee.marquee-active .marquee-inner { | |
@apply flex-none; | |
animation: marquee-horizontal var(--marquee-duration, 10s) linear forwards; | |
animation-delay: 700ms; | |
animation-play-state: var(--marquee-play); | |
} | |
.marquee.marquee-hover-pause.marquee-active:hover .marquee-inner { | |
animation-play-state: paused; | |
} | |
@keyframes marquee-horizontal { | |
0% { | |
transform: translateX(0); | |
} | |
100% { | |
transform: translateX(var(--marquee-offset)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import clsx from 'clsx' | |
import React from 'react' | |
import {useDimensions} from '../hooks/use-dimensions' | |
interface MarqueeProps { | |
active?: boolean | |
speed?: number | |
hoverToPause?: boolean | |
} | |
export const Marquee: React.FC<MarqueeProps> = ({ | |
children, | |
active = true, | |
speed = 20, | |
hoverToPause = true, | |
}) => { | |
const [outerRef, outerDimensions] = useDimensions([active]) | |
const [innerRef, innerDimensions] = useDimensions([active]) | |
const offset = Math.ceil(innerDimensions.width - outerDimensions.width) | |
const duration = offset / speed | |
return ( | |
<div | |
ref={outerRef} | |
className={clsx( | |
'marquee', | |
active && 'marquee-active', | |
hoverToPause && 'marquee-hover-pause', | |
)} | |
> | |
<div | |
ref={innerRef} | |
className="marquee-inner" | |
style={{ | |
['--marquee-play' as string]: active ? 'running' : 'paused', | |
['--marquee-duration' as string]: `${duration}s`, | |
['--marquee-offset' as string]: `-${offset}px`, | |
}} | |
> | |
{children} | |
</div> | |
</div> | |
) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {isEqual} from 'lodash-es' | |
import {DependencyList, RefObject, useEffect, useRef, useState} from 'react' | |
type Dimensions = {width: number; height: number} | |
export const useDimensions = ( | |
deps?: DependencyList, | |
): [ref: RefObject<HTMLDivElement>, dimensions: Dimensions] => { | |
const [dimensions, setDimensions] = useState<Dimensions>({width: 0, height: 0}) | |
const ref = useObserveDimensions(setDimensions, deps) | |
return [ref, dimensions] | |
} | |
export const useObserveDimensions = ( | |
callback: (dimensions: Dimensions) => void, | |
deps?: DependencyList, | |
) => { | |
const ref = useRef<HTMLDivElement>(null) | |
const dimensionsRef = useRef({width: 0, height: 0}) | |
const element = ref.current | |
useEffect(() => { | |
if (!element) return | |
const updateDimensions = ({width, height}: Dimensions) => { | |
const dimensions: Dimensions = {width, height} | |
if (!isEqual(dimensions, dimensionsRef.current)) { | |
callback?.(dimensions) | |
} | |
} | |
if (typeof ResizeObserver === 'undefined') { | |
updateDimensions(element.getBoundingClientRect()) | |
return | |
} | |
const observer = new ResizeObserver(([entry]) => { | |
updateDimensions(entry.contentRect) | |
}) | |
observer.observe(element) | |
return () => { | |
observer.disconnect() | |
} | |
}, [element, callback, ...(deps ?? [])]) | |
return ref | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment