CSS

[React, CSS] 따라다니는 (동적) 사이드 바 만들기

마뇨기 2024. 1. 16. 00:19

 

요즘 동심에 빠진 것 같아. 유튜브에 이런 플레이리스트가 자꾸 떠,,


 

프로젝트 진행 중에 좌측 여백 공간에 스크롤에 따라다니는 사이드 바를 넣어야 했다.

 

 

   좌측 공간에 사이드 바 고정하기   

 

position: fixed;
left: 20px; /* 왼쪽 여백 */
top: 50%;
transform: translateY(-50%);

 

top: 50%

- top 속성은 부모 요소에 대한 요소의 상대적 위치를 지정.

- 요소의 상단이 부모 컨테이너의 높이의 50% 지점에 위치하도록 함.

- 즉, 요소는 부모 컨테이너의 상단에서부터 시작하여 중간 지점까지 배치된다.

 

transform: translateY(-50%);

- transform 속성은 요소를 변형시키는 데 사용되며, translateY는 요소를 수직 방향으로 이동시킨다.

- translateY(-50%)는 요소를 자신의 높이의 50%만큼 위로 이동시킨다. 이는 요소의 중앙이 top 속성에 의해 지정된 위치에 오도록 하기 위해 사용된다.

 

top와 transform을 같이 사용하는 이유를 다시 정리하면,

 

top: 50%;만 사용하면 요소의 상단이 컨테이너의 중간에 위치하게 되어, 전체 요소는 중심보다 아래에 위치하게 된다.

하지만, transform: translateY(-50%);를 추가함으로써, 요소는 자신의 높이의 절반만큼 위로 이동하여, 전체 요소의 중심이 컨테이너의 중앙에 오도록 조정된다.

 

스크롤에 따라 움직이는 사이드 바

 

 


 

 

이렇게 딱 고정되어 움직이면 움직임이 부자연스럽고, 뷰포트 기준으로 가운데 위치하여 배너나 Footer를 침범할 수 있다.

 

부드러운 움직임을 위해선 CSS의 transition 속성을 적용하면 된다.

하지만, transition은 특정 속성 값의 변화에 따라 일어나기 때문에 스크롤 작동 시 top의 값도 변경되어야 한다.

즉, top: 50%;으로 지정한다면 top의 값이 고정되기 때문에 transition을 사용할 수 없다.

 

 

   스크롤에 따라 top 속성값 변경하기 (useEffect)   

 

transition을 사용하기 위해서는 스크롤에 따라서 top의 속성값을 변경해야 한다.

 

하지만, top 속성값이 변경된다면 position: fixed; 를 사용할 수 없다. position: fixed 로 설정된 요소는 브라우저의 뷰포트에 대해 고정된다. 즉, 스크롤과 관계없이 항상 같은 위치에 표시한다.

 

반면 position: absolute; 는 가장 가까운 위치 지정된 (relative, absolute, fixed, 또는 sticky로 설정된) 부모 요소에 대해 상대적으로 배치된다. 만약 그런 조상 요소가 없다면, <html> 요소에 대해 상대적으로 배치된다.

 

따라서, 브라우저의 전체 영역에 따라 위치가 바뀔 수 있도록 position: absolute; 를 사용해보자.

 


 

 

우선 기존의 스타일에서 아래처럼 수정한다.

position: absolute; /* absoulte로 변경 */
left: 20px;
transform: translateY(-50%);
transition: top 0.7s ease-out; /* transition 추가 */

/* top 속성은 제거 */

 

 

그리고 아래처럼 JS 코드를 추가한다.

const [barPosition, setBarPosition] = useState(510);

const handleScroll = () => {
  const position = 956 < 510 + window.scrollY ? 956 : 510 + window.scrollY;
  setBarPosition(position);
};

useEffect(() => {
  window.addEventListener("scroll", handleScroll);

  return () => {
    window.removeEventListener("scroll", handleScroll);
  };
}, []);


...


<div className="side-bar" style={{ top: barPosition }}>
  {/* 내용 */}
</div>

 window.scrollY: 현재 문서가 수직으로 얼마나 스크롤되었는지를 나타냄.

 

 

코드를 들여다보자

const handleScroll = () => {
  const position = 956 < 510 + window.scrollY ? 956 : 510 + window.scrollY;
  setBarPosition(position);
};

 

코드를 보면, window.scrollY 를 통해 스크롤된 만큼 사이드 바도 수직으로 움직인다.

단순히 스크롤에 따라 움직이면 다른 영역을 침범할 수 있다. 프로젝트에서는 상단의 header와 banner, 하단의 footer 사이 공간에서 사이드 바가 움직여야 했다.

 

 

 

1. 먼저, 사이드 바의 위치를 상단 영역 아래부터 시작할 수 있도록 임의 값을 지정한다. (여기서는 510)

2. 그리고 스크롤이 된 만큼 위치가 변해야 하기 때문에 임의 값에 window.scrollY 을 더해준다. (510 +  window.scrollY)

3. 이렇게만 구현하면 하단 영역을 침범할 수 있기 때문에 일정 값을 벗어나지 않도록 한다.

    (956 < 510 + window.scrollY ? 956)

 

 

useEffect(() => {
  window.addEventListener("scroll", handleScroll);

  return () => {
    window.removeEventListener("scroll", handleScroll);
  };
}, []);


...


<div className="side-bar" style={{ top: barPosition }}>
  {/* 내용 */}
</div>

 

이벤트 리스너인 scroll 을 통해 handleScroll 함수를 작동시키고, barPostion 값을 사이드 바의 top 속성값으로 지정해 주면 끝!