Skip to content

색상 영역 컴포넌트

eVe68 edited this page Aug 25, 2024 · 5 revisions

Issue

  • 색상 영역 컴포넌트는 스크롤에 따라 변화하는 화면을 구현
  • 사용자 화면 높이에 따라 오차가 발생하지 않도록 스크롤 계산 기준을 설정해야 함
  • 색상 네비게이션을 구현하여 사용자가 원하는 색상 영역으로 이동 가능하도록 해야 함

어떻게 자연스러운 스크롤을 구현할까?

  • 섹상 영역을 지정해 내부 스크롤을 사용하면 스크롤이 끊김
    • UX 적으로 좋은 경험이 전혀 아니었음
  • 자연스럽게 아래로 내리는 과정에서 색상이 변경되었으면 좋겠음
  • 섹션 내부 스크롤이 아닌 메인 화면 스크롤을 그대로 이용
    • 메인 화면 스크롤을 이용할 때 위에서부터의 절대값을 이용하면 사용자에 따라 계산에 오차가 있을 수 있음을 고려해야 했음

어떻게 색상이 선형적으로 변화할까?

  • 처음에는 자동차 이미지와 배경을 따로 두었음
  • 배경과 자동차 이미지가 동시에 동기화되어 변화해야 함
    • 배경과 자동차가 하나인 하나의 이미지를 사용하여 구현
  • 르노삼성 홈페이지를 디버깅해 보았을 때 CSS의 마스크를 사용하여 이미지를 자름
    • CSS clip-path 속성을 사용
    • inset(0 0 ${변수}% 0) 을 통해 변수 값을 변경하면 아래부터 잘려 보이는 비율을 조절

스크롤과 색상 변경을 어떻게 동기화할까?

  • 스크롤 상태에 맞추어 요소가 변화해야 함
  • 스크롤 상태에 따른 지속적인 리렌더링이 필수적이라고 생각함
    • requestAnimationFrame을 사용하기로 결정
useEffect(() => {
    requestAnimationRef.current = requestAnimationFrame(render);
    return () => {
      cancelAnimationFrame(requestAnimationRef.current);
    };
}, []);

requestAnimationFrame

  • 시스템이 프레임을 그릴 준비가 되면 애니메이션 프레임을 호출하여 애니메이션 웹페이지를 보다 원활하고 효율적으로 생성할 수 있도록 함
  • 실제 화면이 갱신되어 표시되는 주기에 따라 함수를 호출해주기 때문에 자바스크립트가 프레임 시작 시 실행되도록 보장함
  • 이를 통해 스크롤이나 애니메이션의 업데이트가 자연스럽고 성능이 최적화된 방식으로 이루어 질 수 있음

스크롤 상태를 어떻게 관리할까?

  • const scrollPosition = -divRef.current.getBoundingClientRect().y; 를 통해 절대적인 스크롤 상태 값을 가져옴
  • divRef를 기준으로 현재 뷰포트의 위치를 가져올 수 있음
  • 이 값에 음수를 취함으로써 scrollPosition 은 색상 영역으로부터 아래로 스크롤된 거리를 나타냄
  • 이 값을 requestAnimationFrame 안에서 사용해 지속적으로 계산하도록 함
const render = () => {
    if (divRef.current) {
        const imageHeight = divRef.current.offsetHeight / colorInfoData.length;
        const scrollPosition = -divRef.current.getBoundingClientRect().y;
        const newClipPosition = colorInfoData.map((_, index) => {
            const imageOffset = (index + 1) * imageHeight;
            if (
                imageOffset < scrollPosition &&
                scrollPosition < imageOffset + imageHeight
            ) {
                return ((scrollPosition - imageOffset) / imageHeight) * 100;
            } else if (imageOffset < scrollPosition) {
                return 100;
            } else {
                return 0;
            }
        });

        setClipPosition(newClipPosition);
    }

    requestAnimationFrame(render);
};

어떻게 동작하게 할까?

  • 결국 메인 페이지 전체에서 자연스럽게 스크롤이 되어야 하므로 레이아웃 영역 자체는 한 페이지가 아닌 겹친 페이지만큼의 길이
    • 표시할 색상이 5가지이므로 5*(화면 높이) 만큼 스크롤이 되어야 함
  • 2장이 겹친 상태에서 스크롤이 될 때 위쪽에 위치한 이미지가 점점 가려지는 방식으로 구현해야 함

모두 absolute로 둔 상태에서 구현

  • 결국 메인 페이지에 종속되어있기 때문에 첫 화면에서부터 떠 있는 문제
  • 이를 해결하기 위해 색상 영역에 종속시키면 스크롤에 따라 그냥 올라가 버리는 문제
    • top-0을 줘서 해결하려 했지만 스크롤을 따라 올라가려다 top 요소에 의해 잡혀 내려오는 듯 덜덜거리는 현상 발생

모두 sticky로 둔 상태에서 구현

  • top을 0으로 설정하고 index에 따라 가려지는 비율을 결정하는 방식
  • 하지만 요소가 겹치지 않아 top-0에 붙는 시점에 이미지가 모두 가려지는 문제

해결책

  • 첫 번째 이미지 뒤에 두번째 이미지가 top-0위치까지 올라온 뒤 첫 번째 이미지를 가리기 시작함
    • 0번 index 요소의 스크롤이 길어졌지만 이외의 해결책을 찾지 못하였음
  • const imageOffset = (index + 1) * imageHeight; 에서 index에 1을 더한 이유임

이미지가 보여지는 방식

  • 이미지 여러장이 겹친 상태에서의 조건부 논리를 통해 어떻게 보여질지 결정함
    • 현재 스크롤 위치가 이미지의 시작과 끝 사이에 있는 경우: (scrollPosition - imageOffset) / imageHeight * 100 계산을 통해 클립 비율을 계산
    • 스크롤 위치가 이미지의 끝을 지나간 경우: 클립 비율을 100%로 설정하여 이미지가 완전히 보이지 않도록 함
    • 스크롤 위치가 이미지의 시작 전에 있는 경우: 클립 비율을 0%로 설정하여 이미지가 전혀 보이도록 함
Clone this wiki locally