활성화된 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,
};
}