참고한 코드이고 살짝 변형했다.
yarn add framer-motion
yarn add throttle-debounce-ts
import {
motion,
MotionProps,
useTransform,
useScroll
} from "framer-motion";
import { throttle } from "throttle-debounce-ts";
import { useEffect, useRef, useState } from "react";
import styled from "styled-components";
const useElementViewportPosition = (ref) => {
const [position, setPosition] = useState([0, 0]);
useEffect(() => {
if (!ref || !ref.current) return;
const pageHeight = document.body.scrollHeight;
const start = ref.current.offsetTop;
const end = start + ref.current.offsetHeight;
setPosition([start / pageHeight, end / pageHeight]);
}, []);
return { position };
}
const slideAnimation = {
variants: {
full: { backgroundColor: "#663399" },
partial: { backgroundColor: "#808080" }
},
initial: "partial",
whileInView: "full",
viewport: { amount: 1, once: true }
};
const data = [
"",
"",
]; // 이미지 주소
const Example = () => {
const ref = useRef(null);
const carouselRef = useRef(null);
const { position } = useElementViewportPosition(ref);
const [carouselEndPosition, setCarouselEndPosition] = useState(0);
const { scrollYProgress, scrollY } = useScroll();
const yValue = useTransform(scrollYProgress,[0,1],[0, 100*5]);
const x = useTransform(scrollYProgress, position, [0, carouselEndPosition]);
useEffect(() => {
if (!carouselRef || !carouselRef.current) return;
const parent = carouselRef.current.parentElement;
const scrollbarWidth =
window.innerWidth - document.documentElement.clientWidth;
const resetCarouselEndPosition = () => {
if (carouselRef && carouselRef.current) {
const newPosition =
carouselRef.current.clientWidth -
window.innerWidth +
scrollbarWidth +
ref.current.offsetLeft * 2;
setCarouselEndPosition(-newPosition);
}
};
resetCarouselEndPosition();
const handleResize = throttle(10, resetCarouselEndPosition);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return (
<Wrapper>
<Section>
-
</Section>
<Gallery ref={ref}>
<Container>
<StickyWrapper>
<h1>
Creating our collections
</h1>
<Carousel ref={carouselRef} style={{ x, paddingLeft: "20%", paddingRight: "20%" }}>
{data.map((i) => (
<CarouselSlide
{...slideAnimation}
key={i}
>
<img src={i} alt=""/>
</CarouselSlide>
))}
</Carousel>
</StickyWrapper>
</Container>
</Gallery>
<Section style={{ marginBottom: "120px" }}>
-
</Section>
</Wrapper>
)
}
export default Example;
const Wrapper = styled.div`
position: relative;
width:100%;
`
const Section = styled.div`
width:60%;
height:100%;
margin:0px auto;
`
const Gallery = styled.div`
position: relative;
width:100%;
`
const Container = styled.div`
margin: 4px auto 0;
width:100%;
height: 200vh;
`
const StickyWrapper = styled.div`
position: sticky;
top: 0;
height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
overflow: hidden;
`
const Carousel = styled(motion.div)`
display: flex;
gap:48px;
img{
height:100%;
}
`
const CarouselSlide = styled(motion.div)`
width: 300px;
height: 300px;
background-color: #808080;
display: flex;
align-items: center;
justify-content: center;
border-radius: 16px;
overflow:hidden;
`