Skip to content

Instantly share code, notes, and snippets.

@maccman
Created October 23, 2022 16:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save maccman/3db4b461903d23c84de302aba396c9d4 to your computer and use it in GitHub Desktop.
Save maccman/3db4b461903d23c84de302aba396c9d4 to your computer and use it in GitHub Desktop.
.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));
}
}
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>
)
}
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