WEB/재밌어서 만드는 것

[React] 브런치 스타일 웹 만들기/ 배경 덮으면서 올라가는 스크롤/progress bar만들기

자바칩 프라푸치노 2022. 12. 14. 14:41

인터렉티브 웹을 공부하다가 브런치 스타일을 한번 따라해보기로 했다. 

결과는 아래 동영상이다. 

 

여기서 내가 고려한 포인트는 아래와 같다. 

1. 사진 배경을 덮으면서 콘텐츠가 올라가게 하기

2. 헤더 부분이 사진 영역에 있을 때는 투명했다가 스크롤을 하면 그대로 안보이고,  글 영역으로 가면 반투명 해지고 progress bar가 나타남

3. 헤더 부분 아이콘이 사진 영역에서는 검정이고 글 영역에서는 하얀색.

4. 페이지 높이에 따른 progress bar 만들기


(전체코드는 맨 아래에)

html은 이렇게 짰다. 

크게 메뉴, 이미지, 콘텐츠 부분으로 div를 두었다. 

   <div>
      <div className='menu'>
        <div className='menubar'>
          <MenuIcon fontSize="large"/>
        </div>

        <div className='progressbar'>
          <span className='progress' width={progressbar}></span>
        </div>
      </Menu>

      <div className='main_img_wrap'>
        <img className='main_img'
          alt=""
          src="https://images.unsplash.com/photo-1669604120699-eeba651ac235?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=774&q=80"
        />
      </div>
      <div className='content_wrap'>
        <div className='content'>
          꽃이 끓는 풀이 사랑의 놀이 청춘 바이며, 힘있다. 피가 뼈 커다란 남는 갑
          생생하며, 이 사람은 그들은 그리하였는가? 모래뿐일 피고, 같은 따뜻한
          오아이스도 같지 가치를 끓는다. 그들의 피가 끓는 때까지 할지니, 낙원을
          날카로우나 철환하였는가? 심장은 열락의 같이 때문이다. 지혜는 가지에
          인류의 작고 봄바람을 역사를 없으면, 싶이 말이다. 이상, 열매를 피가
          맺어, 우는 앞이 그림자는 힘차게 봄바람이다. 있는 갑 없으면,
          봄바람이다. 위하여 눈에 크고 수 모래뿐일 할지니, 맺어, 기관과 창공에
          황금시대다. 청춘 살 인생을 것이 생의 청춘의 이것이다. 얼마나 피고,
          있는 피고 피는 붙잡아 위하여 새가 약동하다. 봄날의 천하를 낙원을
          인간의 하는 아니더면, 것은 것이다. 예가 가는 이것을 길을 대중을 보이는
          인간은 찬미를 기쁘며, 뿐이다. 얼음에 힘차게 것은 무엇을 실현에 같이,
          있는 것이다. 것은 가슴에 용감하고 것이다.보라, 목숨이 작고 위하여서,
          얼음 사막이다. 곳으로 곳이 따뜻한 듣기만 이상, 힘있다. 가장 얼마나
          창공에 용감하고 귀는 풀밭에 그들을 얼음 것이다. 불러 너의 피에 인생을
          실현에 공자는 그들은 것이다. 주며, 우리의 미묘한 지혜는 그들에게
          영원히 이것이다. 보배를 못할 만물은 거선의 낙원을 작고 칼이다. 대한
          군영과 쓸쓸한 그러므로 용감하고 있는 방황하여도, 끓는다. 능히 그림자는
          눈이 말이다. 피부가 생명을 어디 풍부하게 힘있다. 불러 인간은 뼈
          봄바람이다. 뜨거운지라, 봄바람을 풀밭에 살 보이는 길지 예수는 그러므로
          열매를 사막이다. 풀이 찾아다녀도, 위하여 것이다. 자신과 우리의 품었기
          맺어, 바이며, 아니더면, 위하여 우리는 아니다. 얼음에 어디 주며, 얼음
          긴지라 그들의 부패뿐이다.
        </div>
      </div>
    </div>

이것을 리액트 + styled.component로 바꾼 것

 <div>
      <Menu position={position}>
        <MenuBar>
          <MenuIcon
            fontSize="large"/>
        </MenuBar>

        <ProgressBar>
          <Progress width={progressbar}></Progress>
        </ProgressBar>
      </Menu>

      <MainImgWrap>
        <MainImg
          alt=""
          src="https://images.unsplash.com/photo-1669604120699-eeba651ac235?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=774&q=80"
        />
      </MainImgWrap>
      <ContentWrap>
        <Content>
          꽃이 끓는 풀이 사랑의 놀이 청춘 바이며, 힘있다. 피가 뼈 커다란 남는 갑
          생생하며, 이 사람은 그들은 그리하였는가? 모래뿐일 피고, 같은 따뜻한
          오아이스도 같지 가치를 끓는다. 그들의 피가 끓는 때까지 할지니, 낙원을
          날카로우나 철환하였는가? 심장은 열락의 같이 때문이다. 지혜는 가지에
          인류의 작고 봄바람을 역사를 없으면, 싶이 말이다. 이상, 열매를 피가
          맺어, 우는 앞이 그림자는 힘차게 봄바람이다. 있는 갑 없으면,
          봄바람이다. 위하여 눈에 크고 수 모래뿐일 할지니, 맺어, 기관과 창공에
          황금시대다. 청춘 살 인생을 것이 생의 청춘의 이것이다. 얼마나 피고,
          있는 피고 피는 붙잡아 위하여 새가 약동하다. 봄날의 천하를 낙원을
          인간의 하는 아니더면, 것은 것이다. 예가 가는 이것을 길을 대중을 보이는
          인간은 찬미를 기쁘며, 뿐이다. 얼음에 힘차게 것은 무엇을 실현에 같이,
          있는 것이다. 것은 가슴에 용감하고 것이다.보라, 목숨이 작고 위하여서,
          얼음 사막이다. 곳으로 곳이 따뜻한 듣기만 이상, 힘있다. 가장 얼마나
          창공에 용감하고 귀는 풀밭에 그들을 얼음 것이다. 불러 너의 피에 인생을
          실현에 공자는 그들은 것이다. 주며, 우리의 미묘한 지혜는 그들에게
          영원히 이것이다. 보배를 못할 만물은 거선의 낙원을 작고 칼이다. 대한
          군영과 쓸쓸한 그러므로 용감하고 있는 방황하여도, 끓는다. 능히 그림자는
          눈이 말이다. 피부가 생명을 어디 풍부하게 힘있다. 불러 인간은 뼈
          봄바람이다. 뜨거운지라, 봄바람을 풀밭에 살 보이는 길지 예수는 그러므로
          열매를 사막이다. 풀이 찾아다녀도, 위하여 것이다. 자신과 우리의 품었기
          맺어, 바이며, 아니더면, 위하여 우리는 아니다. 얼음에 어디 주며, 얼음
          긴지라 그들의 부패뿐이다.
        </Content>
      </ContentWrap>
    </div>

 

1. 배경을 덮으면서 콘텐츠가 올라가게 하기

아주 간단하다.

이미지를 position fixed로 두고

그 위에 올라오는 콘텐츠 div를 position relative로 준 다음 z-index를 높게 설정해주면 된다. 

const MainImgWrap = styled.div`
  background-color: black;
`;

const MainImg = styled.img`
  width: 100vw;
  height: 100vh;
  object-fit: cover;
  position: fixed;
  top: 0;
`;

const ContentWrap = styled.div`
  position: relative;
  width: 100%;
  margin-top: 800px;
  background-color: #fff;
  z-index: 1;
`;

const Content = styled.pre`
  z-index: 10;
  width: 600px;
  height: 10000px;
  margin: 0 auto;
  white-space: pre-line;
  line-height: 40px;
  margin-top: 100vh;
  background-color: white;
  font-size: 1.2rem;
`;

 

2. 헤더부분 만들기

헤더부분은 스크롤의 위치에 따라 속성을 다르게 주어야 하기 때문에 맨 처음에  useEffect에서 스크롤 이벤트를 등록한다.

스크롤에 따라서 메뉴가 나오냐 안나오냐도 판단해야하고, 프로그래스 바도 만들어야하기 때문이다. 

  useEffect(() => {
    window.addEventListener("scroll", updateScroll);
  }, []);

 

헤더 부분은 초기에 사진 영역에 있을때는 스크롤 했을 때 사진이랑 같이 그대로 올라가서 안보였다가,

사진이 끝나면 반투명한 배경을 가지면서 컨텐츠 영역의 스크롤을 계속 내려도 fixed로 계속 보이게 된다. 

  const [position, setPosition] = useState("absolute");

 

scrollY 가 사진크기보다 커지면 position을 fixed로 바꿔주었다.

지금은 사진 크기가 윈도우에 꽉 차기 때문에 window.visualViewport.height를 썼다.

const updateScroll = () => {
    if (window.scrollY > window.visualViewport.height) {
      setPosition("fixed");
    } else {
      setPosition("absolute");
    }


  };

그리고 메뉴에 props로 position을 넘겨주고 styled.component에서 받아서 css를 바꿔주었다.

메뉴 아이콘 색상도 사진 영역에 있을 때는 화이트였다가, 글 영역으로 왔을때는 검정색으로 바꿔주었다. 

  
  <Menu position={position}>
        <MenuBar>
          <MenuIcon
            fontSize="large"
            sx={{ color: position === "fixed" ? "black" : "white" }}
          />
        </MenuBar>

        <ProgressBar>
          <Progress width={progressbar}></Progress>
        </ProgressBar>
 </Menu>

 

const Menu = styled.div`
  background-color: ${(props) =>
    props.position === "fixed" ? "white" : "none"};
  z-index: 10;
  position: ${(props) => props.position};
  width: 100vw;
  top: 0;
  opacity: 0.7;
`;

 

3. 프로그래스 바 만들기

progress 바의 색상이 스크롤을 할때마다 채워져야하기 때문에 updateScroll에 프로그래스바 영역을 바꾸는 코드를 추가해주었다.

전체 화면 높이 : document.body.scrollHeight - window.outerHeight

내가 지금 있는 스크롤 위치 : window.scrollY

이것을 나누어서 퍼센트로 만들어 width 설정

  const [progressbar, setProgressBar] = useState(0);

  const updateScroll = () => {
    if (window.scrollY > window.visualViewport.height) {
      setPosition("fixed");
    } else {
      setPosition("absolute");
    }

    setProgressBar(
      Math.ceil(
        (window.scrollY / (document.body.scrollHeight - window.outerHeight)) *
          100
      )
    );
  };

 

props로 width를 넘겨서 css를 설정해주었다. 

 <ProgressBar>
  <Progress width={progressbar}></Progress>
</ProgressBar>
const ProgressBar = styled.div`
  height: 2px;
`;
const Progress = styled.span`
  background: black;
  width: ${(props) => props.width}%;
  display: block;
  height: 2px;
`;

 

 


후기

스크롤 이벤트를 통해서 다양하게 인터렉션을 만들 수 있는데 이것을 안해봤을 때는 멀게만 느껴졌는데 생각보다 쉬워서 더 다양한 것들을 시도해보고 싶다. 애니메이션을 깊게 들어가면 수학적인 공식을 많이 쓰는 것 같은데.. 수학을 안한지 오래됐는데 가능할까?!

728x90