From d74275acbd8641a4ff77acea080494df7d20f539 Mon Sep 17 00:00:00 2001 From: Jay Sheen Date: Fri, 17 Oct 2025 15:13:56 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=A4=20=EB=8F=99=EC=9E=91=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 메뉴 버튼과 스크롤 위치 동기화 문제 해결 - 스크롤 업/다운 시 다른 오프셋 적용으로 정확한 카테고리 선택 - 버튼 클릭 시 해당 카테고리로 정확한 스크롤 이동 - 콘솔 로그 제거 및 코드 정리 - 상수 추출 및 타입 정의 개선 - 불필요한 import 및 변수 제거 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/entities/menu/ui/menu-category.tsx | 10 +- src/shared/ui/menu/index.tsx | 261 +++++++++++++------------ 2 files changed, 145 insertions(+), 126 deletions(-) diff --git a/src/entities/menu/ui/menu-category.tsx b/src/entities/menu/ui/menu-category.tsx index 888ba8d..0ed5c91 100644 --- a/src/entities/menu/ui/menu-category.tsx +++ b/src/entities/menu/ui/menu-category.tsx @@ -137,9 +137,13 @@ export const MenuCategory = ({ return ( <> -
{ buttonRefs.current[itemIndex] = element } } +
{ + if (element) { + buttonRefs.current[itemIndex] = element; + } + } } >
diff --git a/src/shared/ui/menu/index.tsx b/src/shared/ui/menu/index.tsx index d235a01..e1596f4 100644 --- a/src/shared/ui/menu/index.tsx +++ b/src/shared/ui/menu/index.tsx @@ -9,13 +9,25 @@ import { FilterMotionDuration, FilterMotionStyle, FilterMotionVariants, MenuItem import { useEffect, useRef, useState } from 'react'; import { useLocation } from 'react-router'; import { setHomeReloadKey } from '@/pages/home/home-page'; -import { P } from 'node_modules/framer-motion/dist/types.d-Cjd591yU'; + +// 상수 정의 +const SCROLL_ANIMATION_DURATION = 800; +const SCROLL_UP_OFFSET_PX = 150; +const BUTTON_SCROLL_OFFSET = 30; + +// 타입 정의 +interface ShortButton { + menuId: string; + menuName: string; + index: number; +} export interface MenuProps { menuOn: boolean; setMenuOn: (menuOn: boolean) => void; favoriteEdit?: boolean; -}; +} + export const Menu = ({ menuOn, setMenuOn, @@ -24,40 +36,68 @@ export const Menu = ({ const userMids = useStore.getState().UserStore.userMids; const location = useLocation(); const { navigate } = useNavigate(); - const userInfo = useStore((state) => state.UserStore.userInfo); - const [shortBtns, setShortBtns] = useState>>([]); - const [editMode, setEditMode] = useState(false); + const [shortBtns, setShortBtns] = useState([]); + const [editMode, setEditMode] = useState(false); const [changeMenuId, setChangeMenuId] = useState(); - const [shortBtnIdx, setShortBtnIdx] = useState(0); - const [shortBoxOnScroll, setShortBoxOnScroll] = useState(false); - const [menuListOnScroll, setMenuListOnScroll] = useState(false); + const [shortBtnIdx, setShortBtnIdx] = useState(0); const buttonRefs = useRef>([]); const scrollRef = useRef(null); const shortBtnScrollRef = useRef(null); + const isButtonScrolling = useRef(false); + const scrollTimer = useRef(null); + const lastScrollTop = useRef(0); const onClickToNavigate = (path: string) => { onClickToMenuClose(); navigate(path); }; + const scrollCategoryButtonToLeft = (index: number) => { + const buttonElement = shortBtnScrollRef.current?.children[index] as HTMLElement; + if (buttonElement && shortBtnScrollRef.current) { + const scrollLeft = buttonElement.offsetLeft - BUTTON_SCROLL_OFFSET; + shortBtnScrollRef.current.scrollTo({ + top: 0, + left: scrollLeft, + behavior: 'smooth' + }); + } + }; + const onClickToMenuNavigate = (menuId: string, index: number) => { - setShortBoxOnScroll(false); - setMenuListOnScroll(false); - let tops = [0, 255, 410, 565, 720, 875, 1030, 1500]; - let lefts = [0, 85, 170, 275, 360, 450, 450, 450]; + isButtonScrolling.current = true; setShortBtnIdx(index); - scrollRef.current?.scrollTo({ - top: tops[index], - left: 0, - behavior: 'smooth' - }); - shortBtnScrollRef.current?.scrollTo({ - top: 0, - left: lefts[index], - behavior: 'smooth' - }); - + scrollCategoryButtonToLeft(index); + + const categoryElement = buttonRefs.current[index]; + if (categoryElement && scrollRef.current) { + const scrollContainer = scrollRef.current; + const containerStyle = window.getComputedStyle(scrollContainer); + const paddingTop = parseFloat(containerStyle.paddingTop) || 0; + const scrollPosition = categoryElement.offsetTop - paddingTop; + + lastScrollTop.current = scrollPosition; + + scrollContainer.scrollTo({ + top: scrollPosition, + left: 0, + behavior: 'smooth' + }); + } + + if (scrollTimer.current) { + clearTimeout(scrollTimer.current); + } + + scrollTimer.current = setTimeout(() => { + if (scrollRef.current) { + const actualScrollTop = scrollRef.current.scrollTop; + lastScrollTop.current = actualScrollTop; + setShortBtnIdx(index); + } + isButtonScrolling.current = false; + }, SCROLL_ANIMATION_DURATION); }; const onClickToMenuClose = () => { if(editMode){ @@ -93,105 +133,85 @@ export const Menu = ({ }; const shortBtnsSetting = () => { - let shortList = []; - for(let i=0;i ({ + menuId: item.menuId, + menuName: item.menuName, + index + })); setShortBtns(shortList); }; - const shortBtnScroll = (e: any) => { - if(shortBoxOnScroll){ - let x = shortBtnScrollRef.current?.scrollLeft; - let top = 0; - if(x){ - if(x < 85){ - top = 0; - setShortBtnIdx(0); - } - else if(x < 170){ - top = 255; - setShortBtnIdx(1); - } - else if(x < 275){ - top = 410; - setShortBtnIdx(2); - } - else if(x < 360){ - top = 565; - setShortBtnIdx(3); - } - else if(x < 450){ - top = 720; - setShortBtnIdx(4); - } - else{ - top = 875; - setShortBtnIdx(5); + const getCurrentCategoryIndex = (scrollTop: number): number => { + if (buttonRefs.current.length === 0 || !scrollRef.current) return 0; + + const containerStyle = window.getComputedStyle(scrollRef.current); + const paddingTop = parseFloat(containerStyle.paddingTop) || 0; + const adjustedScrollTop = scrollTop + paddingTop; + const isScrollingUp = scrollTop < lastScrollTop.current; + + if (isScrollingUp) { + // 스크롤 업: 화면 상단 기준으로 카테고리 선택 + const checkPoint = scrollTop + paddingTop + SCROLL_UP_OFFSET_PX; + + for (let i = buttonRefs.current.length - 1; i >= 0; i--) { + const element = buttonRefs.current[i]; + if (!element) continue; + + const currentTop = element.offsetTop; + + if (checkPoint >= currentTop) { + if (i < buttonRefs.current.length - 1) { + const nextElement = buttonRefs.current[i + 1]; + if (nextElement && checkPoint >= nextElement.offsetTop) { + continue; + } + } + return i; + } + } + } else { + // 스크롤 다운: 카테고리 시작점 기준 + for (let i = buttonRefs.current.length - 1; i >= 0; i--) { + const element = buttonRefs.current[i]; + if (!element) continue; + + const currentTop = element.offsetTop; + + if (adjustedScrollTop >= currentTop) { + if (i === buttonRefs.current.length - 1) { + return i; + } + + const nextElement = buttonRefs.current[i + 1]; + if (nextElement) { + const nextTop = nextElement.offsetTop; + if (adjustedScrollTop < nextTop) { + return i; + } + } else { + return i; + } } } - - scrollRef.current?.scrollTo({ - top: top, - left: 0, - behavior: 'smooth' - }); } + + return 0; }; - + const menuListScroll = () => { - if(menuListOnScroll){ - let y = scrollRef.current?.scrollTop; - let left = 0; - if(y){ - if(y < 255){ - left = 0; - } - else if(y < 410){ - left = 85; - } - else if(y < 565){ - left = 170; - } - else if(y < 720){ - left = 275; - } - else if(y < 875){ - left = 360; - } - else if(y < 1030){ - left = 450; - } - else{ - left = 450; - } - } - shortBtnScrollRef.current?.scrollTo({ - top: 0, - left: left, - behavior: 'smooth' - }); - } - }; - const shortBoxTouchStart = () => { - setShortBoxOnScroll(true); - setMenuListOnScroll(false); - }; - const shortBoxTouchEnd = () => { + if (isButtonScrolling.current) return; - }; - const menuListTouchStart = () => { - setShortBoxOnScroll(false); - setMenuListOnScroll(true); - }; - const menuListTouchEnd = () => { + const scrollTop = scrollRef.current?.scrollTop || 0; + const currentIndex = getCurrentCategoryIndex(scrollTop); + if (currentIndex !== shortBtnIdx) { + setShortBtnIdx(currentIndex); + scrollCategoryButtonToLeft(currentIndex); + } + + lastScrollTop.current = scrollTop; }; - + useEffect(() => { shortBtnsSetting(); }, []); @@ -255,31 +275,26 @@ export const Menu = ({
-
{ shortBtns.map((value, index) => ( - onClickToMenuNavigate(value.menuId, value.index) } - >{ value.menuName } + onClick={ () => onClickToMenuNavigate(value.menuId, index) } + >{ value.menuName } )) }
- -
{ getMenuCategory() }