react framer-motion 중간에 가로 스크롤로 변환되는 효과

해적왕·2022년 7월 20일
0
post-custom-banner

https://codesandbox.io/s/framer-motion-scroll-linked-animation-useviewportscroll-usetransform-and-custom-useelementviewportposition-hook-p25mx9

참고한 코드이고 살짝 변형했다.

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;
`
profile
프론트엔드
post-custom-banner

0개의 댓글