Last active
April 27, 2023 09:40
react-marquee
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
/* eslint-disable @next/next/no-img-element */ | |
import React, { useRef } from 'react'; | |
import cx from 'classnames'; | |
import useIntersectionObserver from './hooks/use-intersection-observer'; | |
type MarqueeProps = { | |
imagesUrls: string[], | |
}; | |
export const ImagesSlider_WithPlainCSS = ({ imagesUrls }: MarqueeProps) => { | |
const ref = useRef<HTMLDivElement | null>(null); | |
const entry = useIntersectionObserver(ref, { rootMargin: "100px 0px" }); | |
const isInViewport = !!entry?.isIntersecting; | |
return ( | |
<> | |
<style jsx global>{` | |
@keyframes scroll-slider { | |
0% { transform: translate3d(0, 0, 0); } | |
100% { transform: translate3d(-33.33%, 0, 0); } | |
} | |
.ImagesSlider--animate { | |
--duration: calc(var(--items-count, 10) * 4s); | |
animation: scroll-slider var(--duration) linear infinite; | |
transform-origin: left; | |
} | |
`}</style> | |
<div ref={ref} className={cx("h-96 w-full", { "invisible": !isInViewport })}> | |
<div | |
className='h-full w-max flex ImagesSlider--animate' | |
style={{ | |
"--items-count": imagesUrls.length, | |
animationPlayState: isInViewport ? 'running' : 'paused', | |
} as React.CSSProperties} | |
> | |
{[...imagesUrls, ...imagesUrls, ...imagesUrls].map((imageUrl, index) => ( | |
<img className='h-full pointer-events-none' key={imageUrl + index} src={imageUrl} alt={"" + index} loading="eager" /> | |
))} | |
</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
/* eslint-disable @next/next/no-img-element */ | |
import React from 'react'; | |
type MarqueeProps = { | |
imagesUrls: string[], | |
}; | |
export const ImagesSlider_PlainCSS_V2 = ({ imagesUrls }: MarqueeProps) => { | |
const style = { | |
"--items-count": imagesUrls.length, | |
} as React.CSSProperties; | |
return ( | |
<> | |
<style jsx global>{` | |
@keyframes ImagesSlider_PlainCSS_V2_scroll { | |
from { transform: translateX(0%); } | |
to { transform: translateX(-100%); } | |
} | |
.ImagesSlider_PlainCSS_V2--animate { | |
--duration: calc(var(--items-count) * 2s); | |
animation: ImagesSlider_PlainCSS_V2_scroll var(--duration) linear 0s infinite; | |
animation-play-state: running; | |
} | |
`}</style> | |
<div className='overflow-x-hidden flex relative w-full transform-none' style={style}> | |
{new Array(2).fill('').map((_, i) => ( | |
<div key={i} className='min-w-full flex-[0_0_auto] z-[1] flex items-center ImagesSlider_PlainCSS_V2--animate'> | |
<div className="flex-[0_0_auto] flex min-w-[auto]"> | |
{imagesUrls.map((imageUrl, index) => ( | |
<div key={imageUrl + "-" + index} className="transform-none"> | |
<img | |
className='h-96 object-cover' | |
src={imageUrl} | |
alt={"" + index} | |
/> | |
</div> | |
))} | |
</div> | |
</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
/* eslint-disable @next/next/no-img-element */ | |
import React from 'react'; | |
import Marquee_RFM from "react-fast-marquee"; | |
type MarqueeProps = { | |
imagesUrls: string[], | |
}; | |
export const ImagesSlider_WithReactFastMarquee = ({ imagesUrls }: MarqueeProps) => { | |
return ( | |
<Marquee_RFM speed={80}> | |
{[...imagesUrls].map((imageUrl, index) => ( | |
<img | |
key={imageUrl + "-" + index} | |
className='h-96 object-cover' | |
src={imageUrl} | |
alt={"" + index} | |
/> | |
))} | |
</Marquee_RFM> | |
); | |
}; |
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
/* eslint-disable @next/next/no-img-element */ | |
import React from 'react'; | |
import { Swiper, SwiperSlide } from 'swiper/react'; | |
import { Autoplay } from 'swiper'; | |
import 'swiper/css'; | |
import 'swiper/css/autoplay'; | |
type MarqueeProps = { | |
imagesUrls: string[], | |
}; | |
export const ImagesSlider_WithSwiper = ({ imagesUrls }: MarqueeProps) => { | |
return ( | |
<> | |
<style jsx global>{` | |
.ImagesSlider_WithSwiper .swiper-wrapper { | |
// transition-timing-function: linear; | |
} | |
.ImagesSlider_WithSwiper .swiper-slide { | |
width: auto; | |
height: 24rem; | |
} | |
`}</style> | |
<Swiper | |
className='ImagesSlider_WithSwiper' | |
modules={[Autoplay]} | |
speed={3900} | |
loop | |
autoplay={{ disableOnInteraction: false, delay: 0 }} | |
// slidesPerView={4} | |
slidesPerView={'auto'} | |
spaceBetween={0} | |
allowTouchMove={false} | |
> | |
{[...imagesUrls, ...imagesUrls].map((imageUrl, index) => ( | |
<SwiperSlide key={imageUrl + "-" + index}> | |
<img | |
className='h-full' | |
src={imageUrl} | |
alt={"" + index} | |
/> | |
</SwiperSlide> | |
))} | |
</Swiper> | |
</> | |
); | |
}; |
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 { useRef } from 'react'; | |
import { useIntersectionObserver } from './use-intersection-observer'; | |
const Section = (props: { title: string; }) => { | |
const ref = useRef<HTMLDivElement | null>(null); | |
const entry = useIntersectionObserver(ref, {}); | |
const isVisible = !!entry?.isIntersecting; | |
console.log(`Render Section ${props.title}`, { isVisible }); | |
return ( | |
<div | |
ref={ref} | |
style={{ | |
minHeight: '100vh', | |
display: 'flex', | |
border: '1px dashed #000', | |
fontSize: '2rem', | |
}} | |
> | |
<div style={{ margin: 'auto' }}>{props.title}</div> | |
</div> | |
); | |
}; | |
export default function Component() { | |
return ( | |
<> | |
{Array.from({ length: 5 }).map((_, index) => ( | |
<Section key={index + 1} title={`${index + 1}`} /> | |
))} | |
</> | |
); | |
} |
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
// credit: https://usehooks-ts.com/react-hook/use-intersection-observer | |
import { RefObject, useEffect, useState } from 'react'; | |
interface Args extends IntersectionObserverInit { | |
freezeOnceVisible?: boolean; | |
} | |
export function useIntersectionObserver( | |
elementRef: RefObject<Element>, | |
{ | |
threshold = 0, | |
root = null, | |
rootMargin = '0%', | |
freezeOnceVisible = false, | |
}: Args, | |
): IntersectionObserverEntry | undefined { | |
const [entry, setEntry] = useState<IntersectionObserverEntry>(); | |
const frozen = entry?.isIntersecting && freezeOnceVisible; | |
const updateEntry = ([entry]: IntersectionObserverEntry[]): void => { | |
setEntry(entry); | |
}; | |
useEffect(() => { | |
const node = elementRef?.current; // DOM Ref | |
const hasIOSupport = !!window.IntersectionObserver; | |
if (!hasIOSupport || frozen || !node) return; | |
const observerParams = { threshold, root, rootMargin }; | |
const observer = new IntersectionObserver(updateEntry, observerParams); | |
observer.observe(node); | |
return () => observer.disconnect(); | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
}, [elementRef?.current, JSON.stringify(threshold), root, rootMargin, frozen]); | |
return entry; | |
} | |
export default useIntersectionObserver; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment