Skip to content

Commit 534e691

Browse files
authored
Merge pull request #40 from prgrms-web-devcourse-final-project/feat/scrollBtn#37
[feat] scroll to top 버튼 구현
2 parents 3d04261 + 0437ab8 commit 534e691

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

src/app/layout.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Metadata } from 'next';
22
import '@/shared/styles/global.css';
33
import { Toaster } from 'react-hot-toast';
4+
import ScrollTopBtn from '@/shared/components/scrollTop/ScrollTopBtn';
45
export const metadata: Metadata = {
56
title: 'SSOUL',
67
description: '칵테일을 좋아하는 사람들을 위한 서비스',
@@ -13,8 +14,8 @@ export default function RootLayout({
1314
}>) {
1415
return (
1516
<html lang="ko-KR">
16-
<body>
17-
{children}
17+
<body className="relative flex flex-col min-h-screen">
18+
<main className="flex-1">{children}</main>
1819
<div id="modal-root"></div>
1920
<Toaster
2021
position="top-center"
@@ -26,6 +27,8 @@ export default function RootLayout({
2627
},
2728
}}
2829
/>
30+
31+
<ScrollTopBtn />
2932
</body>
3033
</html>
3134
);
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
'use client';
2+
3+
import Arrow from '@/shared/assets/icons/arrow_up_24.svg';
4+
import { useEffect, useRef } from 'react';
5+
6+
function ScrollTopBtn() {
7+
const animationRef = useRef<number | null>(null);
8+
9+
// scrollTop 버튼 클릭 시
10+
const scrollToTop = () => {
11+
const currentPosition = document.documentElement.scrollTop || document.body.scrollTop;
12+
if (currentPosition > 0) {
13+
animationRef.current = requestAnimationFrame(scrollToTop);
14+
window.scrollTo(0, currentPosition - currentPosition / 8);
15+
}
16+
};
17+
18+
// 사용자 스크롤 시 애니메이션 취소
19+
useEffect(() => {
20+
const cancelScroll = () => {
21+
if (animationRef.current) {
22+
cancelAnimationFrame(animationRef.current);
23+
animationRef.current = null;
24+
}
25+
};
26+
27+
window.addEventListener('wheel', cancelScroll, { passive: true });
28+
window.addEventListener('touchstart', cancelScroll, { passive: true });
29+
30+
return () => {
31+
window.removeEventListener('wheel', cancelScroll);
32+
window.removeEventListener('touchstart', cancelScroll);
33+
};
34+
}, []);
35+
36+
// if (!isVisible) return null;
37+
return (
38+
<div className="fixed right-6 bottom-6 z-50">
39+
<button
40+
type="button"
41+
aria-label="최상단으로 스크롤 이동"
42+
onClick={scrollToTop}
43+
className="flex-center w-10 h-10 shadow-[0_4px_12px_0_rgba(0,0,0,0.5)] bg-secondary rounded-full"
44+
>
45+
<Arrow aria-hidden />
46+
</button>
47+
</div>
48+
);
49+
}
50+
export default ScrollTopBtn;

0 commit comments

Comments
 (0)