From fd502d71b2d0a32929ce6928ffe2de1cddcfe26a Mon Sep 17 00:00:00 2001 From: Jay Sheen Date: Thu, 16 Oct 2025 18:05:24 +0900 Subject: [PATCH] =?UTF-8?q?=ED=95=98=EB=8B=A8=20=ED=83=AD=EB=B0=94=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EA=B8=B0=EB=B0=98=20=EC=88=A8?= =?UTF-8?q?=EA=B9=80/=ED=91=9C=EC=8B=9C=20=ED=9A=A8=EA=B3=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Facebook 스타일의 스크롤 인터랙션 적용: - 스크롤 다운 시 탭바 숨김 (translateY + opacity 애니메이션) - 스크롤 업 시 탭바 표시 - requestAnimationFrame으로 성능 최적화 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/shared/ui/assets/css/style.css | 12 ++++++- src/widgets/navigation/footer.tsx | 54 ++++++++++++++++-------------- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/shared/ui/assets/css/style.css b/src/shared/ui/assets/css/style.css index 1266d8c..59c2761 100644 --- a/src/shared/ui/assets/css/style.css +++ b/src/shared/ui/assets/css/style.css @@ -296,6 +296,11 @@ footer { align-items: center; z-index: 1000; border-top: 0.1px solid var(--color-E5E5E5); + transition: transform 0.3s ease-in-out; +} + +.bottom-tabbar.hidden { + transform: translateY(100%); } .tab-button { @@ -308,9 +313,14 @@ footer { background: none; border: none; cursor: pointer; - transition: all 0.2s ease; + transition: all 0.2s ease, opacity 0.3s ease-in-out; padding: 8px 4px; box-sizing: border-box; + opacity: 1; +} + +.bottom-tabbar.hidden .tab-button { + opacity: 0.1; } .tab-button:hover { diff --git a/src/widgets/navigation/footer.tsx b/src/widgets/navigation/footer.tsx index 3653ea0..400ae1a 100644 --- a/src/widgets/navigation/footer.tsx +++ b/src/widgets/navigation/footer.tsx @@ -8,13 +8,14 @@ import { import { useEffect, useState } from 'react'; import { useAppBridge } from '@/hooks'; -export const FooterNavigation = ({ +export const FooterNavigation = ({ setMenuOn, footerCurrentPage, setFavoriteEdit }: FooterProps) => { const { navigate } = useNavigate(); const [isFooterOn, setIsFooterOn] = useState(true); + const [isFooterVisible, setIsFooterVisible] = useState(true); const onClickToNavigate = (path?: string) => { if(!!path){ @@ -97,39 +98,42 @@ export const FooterNavigation = ({ }; useEffect(() => { - let previousTouch: any; + let lastScrollY = 0; + let ticking = false; - const handleTouchStart = (e: TouchEvent) => { - const touch: Touch | undefined = e.touches[0]; - previousTouch = touch; + const handleScroll = () => { + const currentScrollY = window.scrollY; + + if (!ticking) { + window.requestAnimationFrame(() => { + // 스크롤 다운 & 일정 거리 이상 스크롤된 경우 -> 숨김 + if (currentScrollY > lastScrollY && currentScrollY > 50) { + setIsFooterVisible(false); + } + // 스크롤 업 -> 표시 + else if (currentScrollY < lastScrollY) { + setIsFooterVisible(true); + } + + lastScrollY = currentScrollY; + ticking = false; + }); + + ticking = true; + } }; - const handleTouchMove = (e: TouchEvent) => { - const touch: Touch | undefined = e.touches[0]; - let movementY: number | undefined; - if(touch && previousTouch){ - movementY = touch.pageY - previousTouch.pageY; - if(movementY > 0){ - setIsFooterOn(false); - } - else{ - setIsFooterOn(true); - } - }; - }; - - window.addEventListener('touchstart', handleTouchStart); - window.addEventListener('touchmove', handleTouchMove); + + window.addEventListener('scroll', handleScroll, { passive: true }); return () => { - window.removeEventListener('touchstart', handleTouchStart); - window.removeEventListener('touchmove', handleTouchMove); + window.removeEventListener('scroll', handleScroll); }; }, []); return ( <> - { isFooterOn && -