Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Metadata } from 'next';
import '@/shared/styles/global.css';
import { Toaster } from 'react-hot-toast';
import ScrollTopBtn from '@/shared/components/scrollTop/ScrollTopBtn';
export const metadata: Metadata = {
title: 'SSOUL',
description: '칵테일을 좋아하는 사람들을 위한 서비스',
Expand All @@ -13,8 +14,8 @@ export default function RootLayout({
}>) {
return (
<html lang="ko-KR">
<body>
{children}
<body className="relative flex flex-col min-h-screen">
<main className="flex-1">{children}</main>
<div id="modal-root"></div>
<Toaster
position="top-center"
Expand All @@ -26,6 +27,8 @@ export default function RootLayout({
},
}}
/>

<ScrollTopBtn />
</body>
</html>
);
Expand Down
3 changes: 3 additions & 0 deletions src/shared/assets/icons/arrow_up_24.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions src/shared/components/scrollTop/ScrollTopBtn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use client';

import Arrow from '@/shared/assets/icons/arrow_up_24.svg';
import { useEffect, useRef } from 'react';

function ScrollTopBtn() {
const animationRef = useRef<number | null>(null);

// scrollTop 버튼 클릭 시
const scrollToTop = () => {
const currentPosition = document.documentElement.scrollTop || document.body.scrollTop;
if (currentPosition > 0) {
animationRef.current = requestAnimationFrame(scrollToTop);
window.scrollTo(0, currentPosition - currentPosition / 8);
}
};

// 사용자 스크롤 시 애니메이션 취소
useEffect(() => {
const cancelScroll = () => {
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
animationRef.current = null;
}
};

window.addEventListener('wheel', cancelScroll, { passive: true });
window.addEventListener('touchstart', cancelScroll, { passive: true });

return () => {
window.removeEventListener('wheel', cancelScroll);
window.removeEventListener('touchstart', cancelScroll);
};
}, []);

// if (!isVisible) return null;
return (
<div className="fixed right-6 bottom-6 z-50">
<button
type="button"
aria-label="최상단으로 스크롤 이동"
onClick={scrollToTop}
className="flex-center w-10 h-10 shadow-[0_4px_12px_0_rgba(0,0,0,0.5)] bg-secondary rounded-full"
>
<Arrow aria-hidden />
</button>
</div>
);
}
export default ScrollTopBtn;