Swiper + scroll, click에 반응하는 navigation bar 구현

Hyein Son·2021년 7월 3일
0
  • 가려진 tab을 클릭했을 때
  • 스크롤의 위치가 tab에 해당하는 contents의 위치로 이동했을 때

활성화된 tab의 위치가 이동해 화면에서 보이도록 구현했다.
ex) 배달의 민족 메뉴 lnb

서로 스타일이 다른 swipe를 여러군데에서 사용하기 때문에 인자로 최초의 swipe width와 활성화된 tab의 index값을 받아 custom hook을 만들었다.


hooks/useLnbTransition.ts

const useLnbTransition = (initialSwiperWidth: number, activeTab?: number) => {
 
  const lnbRef = useRef<HTMLDivElement>(null);
  // lnb 전체 길이
  const [lnbFullWidth, setLnbFullWidth] = useState(initialSwiperWidth);
  // 화면에 보이는 lnb 길이의 절반
  const [lnbHarfWidth, setLnbHarfWidth] = useState(0);

  
  // tab의 .swiper-slide element 유사배열객체
  const tabNodes = lnbRef.current?.children[0].children[0].children;
  // 유사배열객체이므로 배열로 변환
  const tabNodesArr: HTMLElement[] = tabNodes ? [].slice.call(tabNodes) : [];

  
  useEffect(() => {
    // tab 각각의 width array
    const itemWidthList = tabNodesArr.map(n => n.offsetWidth);

    // tab의 길이를 모두 더해 lnb 전체 길이 계산
    setLnbFullWidth(
      itemWidthList.reduce((w1, w2) => {
        return w1 + w2;
      }, initialSwiperWidth),
    );

    // lnb의 offsetWidth의 절반
    setLnbHarfWidth((lnbRef.current?.offsetWidth as number) / 2);
  }, [tabNodesArr.length]);

  
  useEffect(() => {
    // 활성화된 탭의 left position
    const targetOffsetLeft = tabNodesArr[activeTab as number]?.offsetLeft;
    // 활성화된 탭의 중간값(탭의 너비 / 2) + 활성화된 탭의 left position
    const targetCenterPos =
      tabNodesArr[activeTab as number]?.offsetWidth / 2 + targetOffsetLeft;

    // swipe의 위치
    let pos = 0;
    
    // 활성화된 탭의 위치 <= 화면에 보이는 lnb 길이의 반
    if (targetCenterPos <= lnbHarfWidth) {
      // swipe 왼쪽 끝
      pos = 0;
     
    // 전체 lnb 너비 - 활성화된 탭의 위치 <= 화면에 보이는 lnb 길이의 반
    } else if (lnbFullWidth - targetCenterPos <= lnbHarfWidth) {
      // swipe 오른쪽 끝
      pos = lnbFullWidth - lnbHarfWidth * 2;
      
    } else {
      // 나머지 경우 swipe 가운데 정렬
      pos = targetCenterPos - lnbHarfWidth;
    }

    
    const swiper = lnbRef.current?.children[0].children[0] as HTMLElement;
    swiper.style.transform = `translate3d(-${pos}px, 0px, 0px)`;
    swiper.style.transitionDuration = '300ms';
    
  }, [activeTab, lnbFullWidth]);

  return {
    lnbRef,
  };
}

참고
스와이프메뉴 클릭하면 가운데오기

0개의 댓글