mirror of
https://github.com/Radiquum/radiquum.github.io.git
synced 2025-09-05 22:15:37 +05:00
112 lines
3.4 KiB
TypeScript
112 lines
3.4 KiB
TypeScript
import Styles from "./Photos.Carousel.module.css";
|
|
|
|
import React, { useCallback, useEffect, useRef } from "react";
|
|
import {
|
|
EmblaCarouselType,
|
|
EmblaEventType,
|
|
EmblaOptionsType,
|
|
} from "embla-carousel";
|
|
import useEmblaCarousel from "embla-carousel-react";
|
|
|
|
const TWEEN_FACTOR_BASE = 0.2;
|
|
|
|
type PropType = {
|
|
slides: string[];
|
|
options?: EmblaOptionsType;
|
|
};
|
|
|
|
const EmblaCarousel: React.FC<PropType> = (props) => {
|
|
const { slides, options } = props;
|
|
const [emblaRef, emblaApi] = useEmblaCarousel(options);
|
|
const tweenFactor = useRef(0);
|
|
const tweenNodes = useRef<HTMLElement[]>([]);
|
|
|
|
const setTweenNodes = useCallback((emblaApi: EmblaCarouselType): void => {
|
|
tweenNodes.current = emblaApi.slideNodes().map((slideNode) => {
|
|
return slideNode.querySelector(".embla__parallax__layer") as HTMLElement;
|
|
});
|
|
}, []);
|
|
|
|
const setTweenFactor = useCallback((emblaApi: EmblaCarouselType) => {
|
|
tweenFactor.current = TWEEN_FACTOR_BASE * emblaApi.scrollSnapList().length;
|
|
}, []);
|
|
|
|
const tweenParallax = useCallback(
|
|
(emblaApi: EmblaCarouselType, eventName?: EmblaEventType) => {
|
|
const engine = emblaApi.internalEngine();
|
|
const scrollProgress = emblaApi.scrollProgress();
|
|
const slidesInView = emblaApi.slidesInView();
|
|
const isScrollEvent = eventName === "scroll";
|
|
|
|
emblaApi.scrollSnapList().forEach((scrollSnap, snapIndex) => {
|
|
let diffToTarget = scrollSnap - scrollProgress;
|
|
const slidesInSnap = engine.slideRegistry[snapIndex];
|
|
|
|
slidesInSnap.forEach((slideIndex) => {
|
|
if (isScrollEvent && !slidesInView.includes(slideIndex)) return;
|
|
|
|
if (engine.options.loop) {
|
|
engine.slideLooper.loopPoints.forEach((loopItem) => {
|
|
const target = loopItem.target();
|
|
|
|
if (slideIndex === loopItem.index && target !== 0) {
|
|
const sign = Math.sign(target);
|
|
|
|
if (sign === -1) {
|
|
diffToTarget = scrollSnap - (1 + scrollProgress);
|
|
}
|
|
if (sign === 1) {
|
|
diffToTarget = scrollSnap + (1 - scrollProgress);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
const translate = diffToTarget * (-1 * tweenFactor.current) * 100;
|
|
const tweenNode = tweenNodes.current[slideIndex];
|
|
tweenNode.style.transform = `translateX(${translate}%)`;
|
|
});
|
|
});
|
|
},
|
|
[]
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (!emblaApi) return;
|
|
|
|
setTweenNodes(emblaApi);
|
|
setTweenFactor(emblaApi);
|
|
tweenParallax(emblaApi);
|
|
|
|
emblaApi
|
|
.on("reInit", setTweenNodes)
|
|
.on("reInit", setTweenFactor)
|
|
.on("reInit", tweenParallax)
|
|
.on("scroll", tweenParallax)
|
|
.on("slideFocus", tweenParallax);
|
|
}, [emblaApi, tweenParallax]);
|
|
|
|
return (
|
|
<div className="embla">
|
|
<div className="embla__viewport" ref={emblaRef}>
|
|
<div className="embla__container">
|
|
{slides.map((index) => (
|
|
<div className="embla__slide" key={index}>
|
|
<div className="embla__parallax">
|
|
<div className="embla__parallax__layer">
|
|
<img
|
|
className="embla__slide__img embla__parallax__img"
|
|
src={index}
|
|
alt="Your alt text"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default EmblaCarousel;
|