Skip to content

Commit 8ee022c

Browse files
committed
Merge remote-tracking branch 'origin/main' into dev
2 parents 0a71cd0 + 524e0f7 commit 8ee022c

File tree

9 files changed

+309
-0
lines changed

9 files changed

+309
-0
lines changed

public/1Stars.png

28.5 KB
Loading

public/2Stars.png

30.8 KB
Loading

src/domains/mypage/components/DeleteAllModal.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ interface Props {
99
onClose: () => void;
1010
setIsModal: Dispatch<SetStateAction<boolean>>;
1111
type: 'myBar' | 'myAlarm';
12+
setIsModal: Dispatch<SetStateAction<boolean>>;
13+
type: 'myBar' | 'myAlarm';
1214
}
1315

1416
function DeleteAllModal({ open, onClose, setIsModal, type }: Props) {
@@ -40,6 +42,8 @@ function DeleteAllModal({ open, onClose, setIsModal, type }: Props) {
4042
onClose={onClose}
4143
onConfirm={type == 'myBar' ? handleBarDelete : handleAlarmDelete}
4244
onCancel={onClose}
45+
onConfirm={type == 'myBar' ? handleBarDelete : handleAlarmDelete}
46+
onCancel={onClose}
4347
></ConfirmModal>
4448
);
4549
}

src/domains/mypage/components/pages/my-alarm/MyAlarm.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use client';
22
import { useState } from 'react';
3+
import { useState } from 'react';
34
import Alarm from '../../Alarm';
45
import { getApi } from '@/app/api/config/appConfig';
56
import TextButton from '@/shared/components/button/TextButton';
@@ -19,6 +20,14 @@ interface MyAlarm {
1920
postTitle: string;
2021
read: boolean;
2122
type: string;
23+
id: number;
24+
message: string;
25+
postCategoryName: string;
26+
postId: number;
27+
postThumbnailUrl: string | null;
28+
postTitle: string;
29+
read: boolean;
30+
type: string;
2231
}
2332

2433
function MyAlarm() {
@@ -45,6 +54,7 @@ function MyAlarm() {
4554
});
4655
};
4756
const items = data?.items ?? [];
57+
const items = data?.items ?? [];
4858

4959
return (
5060
<section>
@@ -60,6 +70,30 @@ function MyAlarm() {
6070
<TextButton className="my-5" onClick={handleDelete}>
6171
전체삭제
6272
</TextButton>
73+
{isModal && (
74+
<DeleteAllModal
75+
open={isModal}
76+
onClose={() => setIsModal(!isModal)}
77+
setIsModal={setIsModal}
78+
type="myAlarm"
79+
/>
80+
)}
81+
<TextButton className="my-5" onClick={handleDelete}>
82+
전체삭제
83+
</TextButton>
84+
</div>
85+
<div className="flex flex-col gap-3">
86+
{items.length !== 0 ? (
87+
items.map(({ id, postId, postTitle, read, message, createdAt }: MyAlarm) => (
88+
<Link href={`/community/${postId}`} key={id} onClick={() => handleRead(id)}>
89+
<Alarm title={postTitle} content={message} createdAt={createdAt} read={read} />
90+
</Link>
91+
))
92+
) : (
93+
<div className="flex justify-center">
94+
<p>알림이 없습니다.</p>
95+
</div>
96+
)}
6397
</div>
6498
<div className="flex flex-col gap-3">
6599
{items.length !== 0 ? (

src/domains/mypage/components/pages/my-bar/MyBar.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use client';
22

3+
34
import { abvMap } from '@/domains/mypage/utills/abvMap';
45
import CocktailCard from '@/domains/shared/components/cocktail-card/CocktailCard';
56
import TextButton from '@/shared/components/button/TextButton';
@@ -37,6 +38,7 @@ function MyBar() {
3738
setIsModal(!isModal);
3839
};
3940

41+
const items = data?.items ?? [];
4042
const items = data?.items ?? [];
4143

4244
return (
@@ -53,7 +55,19 @@ function MyBar() {
5355
<TextButton className="my-5" onClick={handleDelete}>
5456
전체삭제
5557
</TextButton>
58+
{isModal && (
59+
<DeleteAllModal
60+
open={isModal}
61+
onClose={() => setIsModal(!isModal)}
62+
setIsModal={setIsModal}
63+
type="myBar"
64+
/>
65+
)}
66+
<TextButton className="my-5" onClick={handleDelete}>
67+
전체삭제
68+
</TextButton>
5669
</div>
70+
{items.length > 0 ? (
5771
{items.length > 0 ? (
5872
<div
5973
className="
@@ -63,6 +77,14 @@ function MyBar() {
6377
md:[grid-template-columns:repeat(3,minmax(0,250px))]
6478
"
6579
>
80+
{items.map(
81+
({
82+
cocktailId,
83+
cocktailName,
84+
imageUrl,
85+
cocktailNameKo,
86+
alcoholStrength,
87+
}: MyCocktail) => {
6688
{items.map(
6789
({
6890
cocktailId,
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useEffect, useRef } from 'react';
2+
3+
function HomeBackground() {
4+
const bgRef = useRef<HTMLDivElement>(null);
5+
6+
useEffect(() => {
7+
const handleMouseMove = (e: MouseEvent) => {
8+
const x = e.clientX / window.innerWidth;
9+
const percentage = 6 + x * 70;
10+
if (bgRef.current) {
11+
bgRef.current.style.background = `linear-gradient(128deg, rgba(26, 26, 26, 0.7) ${percentage}%, rgba(42, 42, 42, 0.3) ${percentage + 10}%, rgba(60, 70, 78, 0) 100%)`;
12+
}
13+
};
14+
15+
window.addEventListener('mousemove', handleMouseMove);
16+
return () => window.removeEventListener('mousemove', handleMouseMove);
17+
}, []);
18+
19+
return (
20+
<div
21+
ref={bgRef}
22+
className="absolute inset-0 z-1 top-0 left-0 transition-all duration-100 will-change-auto"
23+
/>
24+
);
25+
}
26+
27+
export default HomeBackground;
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
'use client';
2+
3+
import { Environment, OrbitControls, useGLTF } from '@react-three/drei';
4+
import { Canvas, useFrame, useThree } from '@react-three/fiber';
5+
import { Bloom, EffectComposer } from '@react-three/postprocessing';
6+
import { useEffect, useRef, useState } from 'react';
7+
import * as THREE from 'three';
8+
9+
function Model({ onLoaded }: { onLoaded: () => void }) {
10+
const { scene } = useGLTF('/3d/model/scene.gltf');
11+
const [scale, setScale] = useState(13);
12+
13+
useEffect(() => {
14+
const isMobile = window.innerWidth < 768; // 모바일 기준 너비
15+
setScale(isMobile ? 3.8 : 11.5); // 모바일이면 작게
16+
}, []);
17+
18+
useEffect(() => {
19+
if (scene) {
20+
onLoaded(); // 모델이 로드되면 부모에게 알림
21+
}
22+
}, [scene]);
23+
24+
if (!scene) return null; // 로딩 전 대기 처리
25+
26+
scene.traverse((child) => {
27+
if ((child as THREE.Mesh).isMesh) {
28+
const mesh = child as THREE.Mesh;
29+
const material = mesh.material as THREE.MeshPhysicalMaterial;
30+
31+
material.envMapIntensity = 3;
32+
material.metalness = 1;
33+
material.roughness = 0.3;
34+
material.emissiveIntensity = 2;
35+
material.clearcoat = 1;
36+
material.clearcoatRoughness = 0.2;
37+
material.needsUpdate = true;
38+
material.opacity = 0.35;
39+
material.bumpScale = 0.3;
40+
material.thickness = 0.1;
41+
}
42+
});
43+
44+
return <primitive object={scene} scale={scale} />;
45+
}
46+
47+
function CameraAnimation() {
48+
const { camera } = useThree();
49+
const targetPosition = new THREE.Vector3(5, 10, 10); // 최종 위치
50+
const startPosition = new THREE.Vector3(0, 15, 6); // 시작 위치
51+
const progress = useRef(0);
52+
53+
useFrame((state, delta) => {
54+
if (progress.current < 1) {
55+
progress.current += delta / 5; // 3초 동안
56+
const t = Math.min(progress.current, 1);
57+
camera.position.lerpVectors(startPosition, targetPosition, t);
58+
}
59+
});
60+
61+
return null;
62+
}
63+
64+
function HomeModel({ onLoaded }: { onLoaded: () => void }) {
65+
return (
66+
<Canvas className="z-10 w-full" camera={{ position: [5, 20, 10], fov: 30 }} dpr={[1, 1.5]}>
67+
<ambientLight intensity={0.5} />
68+
<pointLight position={[10, 30, 40]} intensity={1} />
69+
<spotLight position={[0, 10, 10]} angle={0.2} penumbra={1} intensity={15} castShadow />
70+
<directionalLight intensity={6} color={0xffffff} position={[10, 40, 100]} />
71+
<Environment files={`/hdri/footprint_court.hdr`} background={false} />
72+
<Model onLoaded={onLoaded} />
73+
<CameraAnimation />
74+
<OrbitControls
75+
enablePan={false}
76+
enableZoom={false}
77+
enableRotate={true}
78+
autoRotate={true}
79+
autoRotateSpeed={-0.4}
80+
target={[0, 0, 0]}
81+
/>
82+
83+
<EffectComposer multisampling={0}>
84+
<Bloom
85+
intensity={0.65}
86+
luminanceThreshold={0.9}
87+
luminanceSmoothing={0.2}
88+
luminanceColor={new THREE.Color(1, 1, 1)}
89+
mipmapBlur
90+
/>
91+
</EffectComposer>
92+
</Canvas>
93+
);
94+
}
95+
96+
export default HomeModel;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use client';
2+
3+
import { useState } from 'react';
4+
import HomeModel from './HomeModel';
5+
import StarMain from './StarMain';
6+
import Spinner from '@/shared/components/spinner/Spinner';
7+
8+
function Landing() {
9+
const [isLoading, setIsLoading] = useState(true);
10+
11+
return (
12+
<>
13+
{isLoading && <Spinner />}
14+
<div className="page-layout max-w-full">
15+
<div className="relative w-full h-[1000px]">
16+
<HomeModel onLoaded={() => setIsLoading(false)} />
17+
{!isLoading && <StarMain />}
18+
</div>
19+
</div>
20+
</>
21+
);
22+
}
23+
24+
export default Landing;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
'use client';
2+
3+
import Image from 'next/image';
4+
import foreStar from '../../../../../public/1Stars.png';
5+
import backStar from '../../../../../public/2Stars.png';
6+
import { useEffect, useRef } from 'react';
7+
import gsap from 'gsap';
8+
9+
function StarMain() {
10+
const background = useRef<HTMLDivElement>(null);
11+
const foreground = useRef<HTMLDivElement>(null);
12+
const mouse = useRef({ x: 0, y: 0 });
13+
const rafId = useRef<number | null>(null);
14+
15+
useEffect(() => {
16+
if (!background.current || !foreground.current) return;
17+
18+
const bgX = gsap.quickSetter(background.current, 'x', 'px');
19+
const bgY = gsap.quickSetter(background.current, 'y', 'px');
20+
const bgRotate = gsap.quickSetter(background.current, 'rotate', 'deg');
21+
22+
const fgX = gsap.quickSetter(foreground.current, 'x', 'px');
23+
const fgY = gsap.quickSetter(foreground.current, 'y', 'px');
24+
const fgRotate = gsap.quickSetter(foreground.current, 'rotate', 'deg');
25+
26+
const update = () => {
27+
const { x, y } = mouse.current;
28+
29+
bgX(x * -2);
30+
bgY(y * -2);
31+
bgRotate(x * -0.2);
32+
33+
fgX(x * 3);
34+
fgY(y * 3);
35+
fgRotate(y * 0.2);
36+
37+
rafId.current = requestAnimationFrame(update);
38+
};
39+
40+
const handleMouseMove = (e: MouseEvent) => {
41+
// 화면 중앙 기준으로 얼마나 벗어났는지 (-1 ~ 1 범위)
42+
const x = (e.clientX / window.innerWidth - 0.5) * 2;
43+
const y = (e.clientY / window.innerHeight - 0.5) * 2;
44+
mouse.current = { x, y };
45+
};
46+
47+
const handleTouchMove = (e: TouchEvent) => {
48+
const touch = e.touches[0];
49+
const x = (touch.clientX / window.innerWidth - 0.5) * 2;
50+
const y = (touch.clientY / window.innerHeight - 0.5) * 2;
51+
mouse.current = { x, y };
52+
};
53+
54+
window.addEventListener('mousemove', handleMouseMove);
55+
window.addEventListener('touchmove', handleTouchMove);
56+
rafId.current = requestAnimationFrame(update);
57+
58+
return () => {
59+
window.removeEventListener('mousemove', handleMouseMove);
60+
window.removeEventListener('touchmove', handleTouchMove);
61+
if (rafId.current) cancelAnimationFrame(rafId.current);
62+
};
63+
}, []);
64+
65+
return (
66+
<>
67+
{/* <HomeBackground /> */}
68+
<div>
69+
<div className="absolute top-0 left-0 w-full h-full overflow-hidden pointer-events-none">
70+
<div
71+
ref={background}
72+
className="absolute w-screen h-screen top-0 left-0 will-change-transform"
73+
>
74+
<Image
75+
src={foreStar}
76+
alt="앞쪽 별"
77+
fill
78+
className="object-cover object-center"
79+
priority
80+
quality={100}
81+
/>
82+
</div>
83+
<div
84+
ref={foreground}
85+
className="absolute w-screen h-screen top-0 left-0 will-change-transform"
86+
>
87+
<Image
88+
src={backStar}
89+
alt="뒤쪽 별"
90+
fill
91+
className="object-cover object-center"
92+
priority
93+
quality={100}
94+
/>
95+
</div>
96+
</div>
97+
</div>
98+
</>
99+
);
100+
}
101+
102+
export default StarMain;

0 commit comments

Comments
 (0)