Skip to content

Commit cd003b6

Browse files
committed
Merge branch 'fix/bug/hyl' of https://github.com/FE9-2/workroot into fix/bug/hyl
2 parents 4511ee1 + 04c80d5 commit cd003b6

File tree

2 files changed

+190
-100
lines changed

2 files changed

+190
-100
lines changed

src/app/(home)/page.tsx

Lines changed: 153 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { motion, AnimatePresence } from "framer-motion";
55
import Image from "next/image";
66
import Lenis from "@studio-freight/lenis";
77
import { useMediaQuery } from "react-responsive";
8+
import TypewriterText from "../components/animation/typewriterText";
89

910
const slides = [
1011
{
@@ -37,7 +38,7 @@ const slides = [
3738
content: "언제든 다시 볼 수 있게\n공고를 스크랩하세요",
3839
image: "/images/land/step2-2.jpg",
3940
blackAreaTitle: "사장님 이용 방법",
40-
blackAreaContent: "워크채널 우측의 [폼 만들기] 버튼을 클릭하고\n인재 채용을 시작하세요",
41+
blackAreaContent: "워크채널 우의 [폼 만들기] 버튼을 클릭하고\n인재 채용을 시작하세요",
4142
blackAreaImage: "/images/land/step2-1.jpg",
4243
},
4344
{
@@ -60,6 +61,21 @@ const bounceAnimation = {
6061
},
6162
};
6263

64+
// 배경 그라데이션 애니메이션 추가
65+
const backgroundVariants = {
66+
initial: {
67+
background: "linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 100%)",
68+
},
69+
animate: {
70+
background: "linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 100%)",
71+
transition: {
72+
duration: 20,
73+
repeat: Infinity,
74+
repeatType: "reverse" as const,
75+
},
76+
},
77+
};
78+
6379
export default function LandingPage() {
6480
const isLargeScreen = useMediaQuery({ minWidth: 641 });
6581
const [currentSlide, setCurrentSlide] = useState(0);
@@ -71,12 +87,12 @@ export default function LandingPage() {
7187
useEffect(() => {
7288
setIsLoaded(true);
7389
lenisRef.current = new Lenis({
74-
duration: 0.8, // 1.2에서 0.8로 변경
75-
easing: (t: number) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
90+
duration: 1.2,
91+
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
7692
smoothWheel: true,
7793
wheelMultiplier: 2,
78-
touchMultiplier: 1.2,
79-
infinite: false,
94+
touchMultiplier: 2,
95+
infinite: true,
8096
});
8197

8298
function raf(time: number) {
@@ -87,36 +103,38 @@ export default function LandingPage() {
87103
requestAnimationFrame(raf);
88104

89105
let lastScrollTime = 0;
90-
const scrollThreshold = 500; // ms
106+
const scrollThreshold = 150;
91107

92108
lenisRef.current.on("scroll", ({ progress }: { progress: number }) => {
93109
const currentTime = Date.now();
94110
if (currentTime - lastScrollTime < scrollThreshold) return;
95111

96112
if (!isScrollingRef.current) {
97113
const totalSlides = slides.length;
98-
const newSlideIndex = Math.min(slides.length - 1, Math.max(0, Math.round(progress * (totalSlides - 1))));
114+
const progressPerSlide = 1 / (totalSlides - 1);
115+
const currentProgress = progress / progressPerSlide;
116+
let newSlideIndex = Math.round(currentProgress);
117+
118+
if (progress >= 0.99) {
119+
newSlideIndex = 0;
120+
setTimeout(() => {
121+
lenisRef.current?.scrollTo(0, {
122+
duration: 1.2,
123+
easing: (t) => t * (2 - t),
124+
});
125+
}, 100);
126+
}
99127

100128
if (newSlideIndex !== currentSlide) {
101129
setCurrentSlide(newSlideIndex);
102130
isScrollingRef.current = true;
103-
const targetScroll =
104-
(newSlideIndex / (totalSlides - 1)) *
105-
(isLargeScreen ? containerRef.current!.scrollHeight : containerRef.current!.scrollWidth);
106-
107-
lenisRef.current?.scrollTo(targetScroll, {
108-
immediate: false,
109-
duration: 600, // 800에서 600으로 변경
110-
easing: (t: number) => t * (2 - t),
111-
});
112-
113-
lastScrollTime = currentTime;
114131

115132
setTimeout(() => {
116133
isScrollingRef.current = false;
117-
}, 600); // 800에서 600으로 변경
134+
}, 200);
118135
}
119136
}
137+
lastScrollTime = currentTime;
120138
});
121139

122140
document.documentElement.style.scrollbarWidth = "none";
@@ -143,34 +161,41 @@ export default function LandingPage() {
143161
<AnimatePresence>
144162
{isLoaded && (
145163
<motion.div
146-
initial={{ opacity: 0 }}
147-
animate={{ opacity: 1 }}
164+
variants={backgroundVariants}
165+
initial="initial"
166+
animate="animate"
148167
exit={{ opacity: 0 }}
149168
transition={{ duration: 1 }}
150169
ref={containerRef}
151-
className={`h-[400vh] min-h-[768px] overflow-hidden bg-gradient-to-br from-[#1a1a1a] to-[#2a2a2a] ${
152-
isLargeScreen ? "" : "flex flex-col"
153-
}`}
170+
className={`relative h-[400vh] min-h-[768px] overflow-hidden ${isLargeScreen ? "" : "flex flex-col"}`}
154171
>
155-
<div className={`fixed inset-0 ${isLargeScreen ? "flex" : "flex flex-col"}`}>
172+
<div className="absolute inset-0 opacity-20">
173+
<div className="particles-container" />
174+
</div>
175+
176+
<motion.div
177+
className={`fixed inset-0 ${isLargeScreen ? "flex" : "flex flex-col"}`}
178+
style={{
179+
backdropFilter: "blur(8px)",
180+
WebkitBackdropFilter: "blur(8px)",
181+
}}
182+
>
156183
<motion.div
157-
className={`relative ${isLargeScreen ? "w-1/2" : "flex h-1/2 w-full items-center justify-center overflow-hidden"}`}
184+
className={`relative ${
185+
isLargeScreen ? "w-1/2" : "flex h-1/2 w-full items-center justify-center overflow-hidden"
186+
}`}
158187
animate={{
159188
width: currentSlide === 0 ? "100%" : isLargeScreen ? "50%" : "100%",
160189
height: currentSlide === 0 ? "100%" : isLargeScreen ? "100%" : "50%",
161190
}}
162191
transition={{ duration: 0.8, ease: "easeInOut" }}
163192
>
164193
<motion.div
165-
className="absolute inset-0 flex items-center justify-center"
166-
initial={{ opacity: 0 }}
194+
className="relative flex h-full w-full items-center justify-center"
167195
animate={{
168-
opacity: 1,
169-
}}
170-
transition={{
171-
duration: 0.8, // Updated transition duration
172-
ease: "easeInOut",
196+
scale: currentSlide === 0 ? 1 : 0.8,
173197
}}
198+
transition={{ duration: 0.8, ease: "easeInOut" }}
174199
>
175200
{currentSlide === 0 ? (
176201
<Image
@@ -179,6 +204,7 @@ export default function LandingPage() {
179204
width={800}
180205
height={800}
181206
className="h-full max-h-[800px] w-full max-w-[800px] object-contain"
207+
priority
182208
/>
183209
) : currentSlide === 4 || currentSlide === 5 ? (
184210
<motion.div
@@ -187,20 +213,20 @@ export default function LandingPage() {
187213
initial={{ opacity: 0, y: 20 }}
188214
animate={{ opacity: 1, y: 0 }}
189215
exit={{ opacity: 0, y: -20 }}
190-
transition={{ duration: 1.2, ease: "easeInOut" }} // Update 1
216+
transition={{ duration: 1.2, ease: "easeInOut" }}
191217
>
192218
<motion.h2
193-
className="mb-2 mt-0 text-center text-xl font-semibold text-gray-100 max-[640px]:mb-1 max-[640px]:px-4 md:mb-4 md:mt-0 md:text-3xl"
219+
className="mb-2 mt-0 text-center text-xl font-semibold text-gray-100 drop-shadow-[0_2px_2px_rgba(0,0,0,0.3)] max-[640px]:mb-1 max-[640px]:px-4 md:mb-4 md:mt-0 md:text-3xl"
194220
initial={{ opacity: 0, y: 20 }}
195221
animate={{ opacity: 1, y: 0 }}
196-
transition={{ delay: 0.3, duration: 0.6 }} // Update 2
222+
transition={{ delay: 0.3, duration: 0.6 }}
197223
>
198-
{slides[currentSlide].blackAreaTitle}
224+
<TypewriterText text={slides[currentSlide].blackAreaTitle || ""} />
199225
</motion.h2>
200226
<motion.div
201227
initial={{ opacity: 0, y: 20 }}
202228
animate={{ opacity: 1, y: 0 }}
203-
transition={{ delay: 0.4, duration: 0.6 }} // Update 2
229+
transition={{ delay: 0.4, duration: 0.6 }}
204230
className="mb-2 w-full max-w-[600px] max-[640px]:max-w-[75%] max-[640px]:px-4 md:mb-4"
205231
style={{ maxHeight: "calc(100% - 12rem)" }}
206232
>
@@ -221,28 +247,27 @@ export default function LandingPage() {
221247
className="mb-0 max-w-[600px] whitespace-pre-wrap text-center text-sm text-gray-200 max-[640px]:mt-1 max-[640px]:px-4 md:text-xl"
222248
initial={{ opacity: 0, y: 20 }}
223249
animate={{ opacity: 1, y: 0 }}
224-
transition={{ delay: 0.5, duration: 0.6 }} // Update 2
250+
transition={{ delay: 0.5, duration: 0.6 }}
225251
>
226-
{slides[currentSlide].blackAreaContent}
252+
<TypewriterText text={slides[currentSlide].blackAreaContent || ""} />
227253
</motion.p>
228254
</motion.div>
229255
) : (
230256
<motion.div
231-
className="absolute inset-0 flex items-center justify-center"
257+
className="relative h-full max-h-[600px] w-full max-w-[600px]"
232258
initial={{ opacity: 0 }}
233259
animate={{ opacity: 1 }}
234260
exit={{ opacity: 0 }}
235-
transition={{ duration: 0.8 }} // Update 3
261+
transition={{ duration: 0.8 }}
236262
>
237-
<div className="relative h-full max-h-[600px] w-full max-w-[600px]">
238-
<Image
239-
src="/brand.png"
240-
alt="Brand Logo"
241-
fill
242-
className="object-contain"
243-
sizes="(max-width: 600px) 100vw, 600px"
244-
/>
245-
</div>
263+
<Image
264+
src="/brand.png"
265+
alt="Brand Logo"
266+
fill
267+
className="object-contain"
268+
sizes="(max-width: 600px) 100vw, 600px"
269+
priority
270+
/>
246271
</motion.div>
247272
)}
248273
</motion.div>
@@ -267,75 +292,103 @@ export default function LandingPage() {
267292
<AnimatePresence mode="wait">
268293
{currentSlide > 0 && (
269294
<motion.div
270-
key={currentSlide}
271-
className={`relative z-40 flex ${
272-
isLargeScreen ? "w-1/2" : "h-1/2 w-full"
273-
} flex-col items-center justify-center p-4 pb-6 pt-4 max-[640px]:px-12 max-[640px]:py-3 md:p-6 md:pb-8 md:pt-6`}
295+
className={`relative z-40 flex ${isLargeScreen ? "w-1/2" : "h-1/2 w-full"}`}
274296
style={{
275297
background: "linear-gradient(135deg, #71db77 0%, #56c45d 100%)",
276298
display: "flex",
277299
flexDirection: "column",
278300
justifyContent: "center",
279301
alignItems: "center",
280302
height: isLargeScreen ? "100%" : "50%",
303+
overflow: "hidden",
281304
}}
282-
initial={{ opacity: 0, [isLargeScreen ? "y" : "x"]: isLargeScreen ? "100%" : "100%" }}
283-
animate={{ opacity: 1, [isLargeScreen ? "y" : "x"]: 0 }}
284-
exit={{ opacity: 0, [isLargeScreen ? "y" : "x"]: isLargeScreen ? "-100%" : "-100%" }}
285-
transition={{ duration: 0.4, ease: "easeInOut" }} // Updated transition duration
305+
initial={{ width: 0 }}
306+
animate={{ width: isLargeScreen ? "50%" : "100%" }}
307+
transition={{ duration: 0.8, ease: "easeInOut" }}
286308
>
287-
<motion.h2
288-
className="mb-2 mt-0 text-center text-xl font-semibold text-[#1a1a1a] max-[640px]:mb-1 max-[640px]:px-4 md:mb-4 md:mt-0 md:text-3xl"
289-
initial={{ opacity: 0, y: 20 }}
290-
animate={{ opacity: 1, y: 0 }}
291-
transition={{ delay: 0.05, duration: 0.3 }} // Updated transition
292-
>
293-
{slides[currentSlide].title}
294-
</motion.h2>
295-
<motion.div
296-
initial={{ opacity: 0, y: 20 }}
297-
animate={{ opacity: 1, y: 0 }}
298-
transition={{ delay: 0.1, duration: 0.3 }} // Updated transition
299-
className="mb-2 w-full max-w-[600px] max-[640px]:max-w-[75%] max-[640px]:px-4 md:mb-4"
300-
style={{ maxHeight: "calc(100% - 12rem)" }}
301-
>
302-
<div
303-
className="relative w-full overflow-hidden rounded-lg shadow-lg"
304-
style={{ paddingBottom: "56.25%" }}
309+
<AnimatePresence mode="wait">
310+
<motion.div
311+
key={currentSlide}
312+
className="flex h-full w-full flex-col items-center justify-center p-4 pb-6 pt-4 max-[640px]:px-12 max-[640px]:py-3 md:p-6 md:pb-8 md:pt-6"
313+
initial={{ y: "100%" }}
314+
animate={{ y: 0 }}
315+
exit={{ y: "-100%" }}
316+
transition={{ duration: 0.4, ease: "easeInOut" }}
305317
>
306-
<Image
307-
src={slides[currentSlide].image || ""}
308-
alt={slides[currentSlide].title || ""}
309-
fill
310-
style={{ objectFit: "cover" }}
311-
sizes="(max-width: 768px) 100vw, 600px"
312-
/>
313-
</div>
314-
</motion.div>
315-
<motion.p
316-
className="mb-0 max-w-[600px] whitespace-pre-wrap text-center text-sm text-[#1a1a1a] max-[640px]:mt-1 max-[640px]:px-4 md:text-xl"
317-
initial={{ opacity: 0, y: 20 }}
318-
animate={{ opacity: 1, y: 0 }}
319-
transition={{ delay: 0.15, duration: 0.3 }} // Updated transition
320-
>
321-
{slides[currentSlide].content}
322-
</motion.p>
318+
<motion.h2
319+
className="mb-2 mt-0 text-center text-xl font-semibold text-[#1a1a1a] max-[640px]:mb-1 max-[640px]:px-4 md:mb-4 md:mt-0 md:text-3xl"
320+
initial={{ opacity: 0, y: 20 }}
321+
animate={{ opacity: 1, y: 0 }}
322+
transition={{ delay: 0.05, duration: 0.3 }}
323+
>
324+
<TypewriterText text={slides[currentSlide].title} />
325+
</motion.h2>
326+
<motion.div
327+
initial={{ opacity: 0, y: 20 }}
328+
animate={{ opacity: 1, y: 0 }}
329+
transition={{ delay: 0.1, duration: 0.3 }}
330+
className="mb-2 w-full max-w-[600px] max-[640px]:max-w-[75%] max-[640px]:px-4 md:mb-4"
331+
style={{ maxHeight: "calc(100% - 12rem)" }}
332+
>
333+
<div
334+
className="relative w-full overflow-hidden rounded-lg shadow-lg transition-shadow duration-300 hover:shadow-2xl"
335+
style={{ paddingBottom: "56.25%" }}
336+
>
337+
<Image
338+
src={slides[currentSlide].image || ""}
339+
alt={slides[currentSlide].title || ""}
340+
fill
341+
style={{ objectFit: "cover" }}
342+
sizes="(max-width: 768px) 100vw, 600px"
343+
className="transition-transform duration-300 hover:scale-105"
344+
/>
345+
</div>
346+
</motion.div>
347+
<motion.p
348+
className="mb-0 max-w-[600px] whitespace-pre-wrap text-center text-sm text-[#1a1a1a] max-[640px]:mt-1 max-[640px]:px-4 md:text-xl"
349+
initial={{ opacity: 0, y: 20 }}
350+
animate={{ opacity: 1, y: 0 }}
351+
transition={{ delay: 0.15, duration: 0.3 }}
352+
>
353+
<TypewriterText text={slides[currentSlide].content} />
354+
</motion.p>
355+
</motion.div>
356+
</AnimatePresence>
323357
</motion.div>
324358
)}
325359
</AnimatePresence>
326-
</div>
360+
</motion.div>
327361

328362
{currentSlide > 0 && (
329-
<div
330-
className={`fixed ${isLargeScreen ? "right-4 top-1/2 -translate-y-1/2 space-y-2" : "bottom-4 left-1/2 flex -translate-x-1/2 space-x-2"}`}
363+
<motion.div
364+
className={`fixed ${
365+
isLargeScreen
366+
? "right-8 top-1/2 -translate-y-1/2 space-y-3"
367+
: "bottom-8 left-1/2 flex -translate-x-1/2 space-x-3"
368+
}`}
331369
>
332370
{slides.slice(1).map((_, index) => (
333-
<div
371+
<motion.div
334372
key={index + 1}
335-
className={`h-4 w-4 rounded-full ${index + 1 === currentSlide ? "bg-[#108b2d]" : "bg-[#f8fff8]"}`}
373+
className="h-3 w-3 cursor-pointer rounded-full border-2 border-white/50"
374+
whileHover={{ scale: 1.2 }}
375+
animate={{
376+
backgroundColor: index + 1 === currentSlide ? "#108b2d" : "transparent",
377+
scale: index + 1 === currentSlide ? 1.2 : 1,
378+
}}
379+
onClick={() => {
380+
if (!isScrollingRef.current) {
381+
const targetScroll =
382+
((index + 1) / (slides.length - 1)) * (containerRef.current?.scrollHeight || 0);
383+
lenisRef.current?.scrollTo(targetScroll, {
384+
duration: 1.2,
385+
easing: (t) => t * (2 - t),
386+
});
387+
}
388+
}}
336389
/>
337390
))}
338-
</div>
391+
</motion.div>
339392
)}
340393
</motion.div>
341394
)}

0 commit comments

Comments
 (0)