diff --git a/script.js b/script.js new file mode 100644 index 0000000..504fc0d --- /dev/null +++ b/script.js @@ -0,0 +1,253 @@ +import gsap from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; +import Lenis from "lenis"; + +document.addEventListener("DOMContentLoaded", () => { + gsap.registerPlugin(ScrollTrigger); + + const lenis = new Lenis(); + lenis.on("scroll", ScrollTrigger.update); + gsap.ticker.add((time) => { + lenis.raf(time * 1000); + }); + gsap.ticker.lagSmoothing(0); + + const smoothStep = (p) => p * p * (3 - 2 * p); + + if (window.innerWidth > 1000) { + // πŸ‘‰ μΉ΄λ“œ id 배열을 λ³€μˆ˜λ‘œ 뢄리 (좔가함) + const heroCardIds = ["#hero-card-1", "#hero-card-2", "#hero-card-3", "#hero-card-4", "#hero-card-5"]; + const servicesCardIds = ["#card-1", "#card-2", "#card-3", "#card-4", "#card-5"]; + + // πŸ‘‰ ν•˜λ“œμ½”λ”© κ°’κ³Ό λ™μΌν•˜κ²Œ λ‚˜μ˜€λ„λ‘ μžλ™ 계산 μˆ˜μ • + const cardCount = heroCardIds.length; + + // Hero μ„Ήμ…˜ - ν•˜λ“œμ½”λ”© κ°’: ["150%", "85%", "0%", "-85%", "-150%"] + const spreadX = heroCardIds.map((_, i) => { + if (i === 0) return "150%"; + if (i === 1) return "85%"; + if (i === 2) return "0%"; + if (i === 3) return "-85%"; + if (i === 4) return "-150%"; + // μΆ”κ°€ μΉ΄λ“œκ°€ μžˆμ„ 경우λ₯Ό μœ„ν•œ 일반 곡식 + const centerIndex = Math.floor(cardCount / 2); + if (i < centerIndex) { + return `${150 - (centerIndex - i - 1) * 65}%`; + } else if (i > centerIndex) { + return `${-150 + (centerIndex - i + 1) * 65}%`; + } + return "0%"; + }); + + // Hero μ„Ήμ…˜ - ν•˜λ“œμ½”λ”© κ°’: [-15, -7.5, 0, 7.5, 15] + const spreadRotate = heroCardIds.map((_, i) => { + if (i === 0) return -15; + if (i === 1) return -7.5; + if (i === 2) return 0; + if (i === 3) return 7.5; + if (i === 4) return 15; + // μΆ”κ°€ μΉ΄λ“œκ°€ μžˆμ„ 경우λ₯Ό μœ„ν•œ 일반 곡식 + const centerIndex = Math.floor(cardCount / 2); + return (i - centerIndex) * 7.5; + }); + + ScrollTrigger.create({ + trigger: ".hero", + start: "top top", + end: "75% top", + scrub: 1, + onUpdate: (self) => { + const progress = self.progress; + + const heroCardsContainerOpacity = gsap.utils.interpolate( + 1, + 0.5, + smoothStep(progress) + ); + gsap.set(".hero-cards", { + opacity: heroCardsContainerOpacity, + }); + + heroCardIds.forEach((cardId, index) => { + const delay = index * 0.9; + const cardProgress = gsap.utils.clamp( + 0, + 1, + (progress - delay * 0.1) / (1 - delay * 0.1) + ); + + const y = gsap.utils.interpolate( + "0%", + "350%", + smoothStep(cardProgress) + ); + const scale = gsap.utils.interpolate( + 1, + 0.75, + smoothStep(cardProgress) + ); + + const x = gsap.utils.interpolate("0%", spreadX[index], smoothStep(cardProgress)); + const rotation = gsap.utils.interpolate(0, spreadRotate[index], smoothStep(cardProgress)); + + gsap.set(cardId, { + y: y, + x: x, + rotation: rotation, + scale: scale, + }); + }); + }, + }); + + ScrollTrigger.create({ + trigger: ".services", + start: "top top", + end: `+=${window.innerHeight * 4}px`, + pin: ".services", + pinSpacing: true, + }); + + ScrollTrigger.create({ + trigger: ".services", + start: "top top", + end: `+=${window.innerHeight * 4}px`, + onLeave: () => { + const servicesSection = document.querySelector(".services"); + const servicesRect = servicesSection.getBoundingClientRect(); + const servicesTop = window.pageYOffset + servicesRect.top; + + gsap.set(".cards", { + position: "absolute", + top: servicesTop, + left: 0, + width: "100vw", + height: "100vh", + }); + }, + onEnterBack: () => { + gsap.set(".cards", { + position: "fixed", + top: 0, + left: 0, + width: "100vw", + height: "100vh", + }); + }, + }); + + ScrollTrigger.create({ + trigger: ".services", + start: "top bottom", + end: `+=${window.innerHeight * 4}`, + scrub: 1, + onUpdate: (self) => { + const progress = self.progress; + + const headerProgress = gsap.utils.clamp(0, 1, progress / 0.9); + const headerY = gsap.utils.interpolate( + "400%", + "0%", + smoothStep(headerProgress) + ); + gsap.set(".services-header", { + y: headerY, + }); + + servicesCardIds.forEach((cardId, index) => { + const delay = index * 0.5; + const cardProgress = gsap.utils.clamp( + 0, + 1, + (progress - delay * 0.1) / (0.9 - delay * 0.1) + ); + + const innerCard = document.querySelector(`${cardId} .flip-card-inner`); + + let y; + if (cardProgress < 0.4) { + const normalizedProgress = cardProgress / 0.4; + y = gsap.utils.interpolate("-100%", "50%", smoothStep(normalizedProgress)); + } else if (cardProgress < 0.6) { + const normalizedProgress = (cardProgress - 0.4) / 0.2; + y = gsap.utils.interpolate("50%", "0%", smoothStep(normalizedProgress)); + } else { + y = "0%"; + } + + let scale; + if (cardProgress < 0.4) { + const normalizedProgress = cardProgress / 0.4; + scale = gsap.utils.interpolate(0.25, 0.75, smoothStep(normalizedProgress)); + } else if (cardProgress < 0.6) { + const normalizedProgress = (cardProgress - 0.4) / 0.2; + scale = gsap.utils.interpolate(0.75, 1, smoothStep(normalizedProgress)); + } else { + scale = 1; + } + + let opacity; + if (cardProgress < 0.2) { + const normalizedProgress = cardProgress / 0.2; + opacity = smoothStep(normalizedProgress); + } else { + opacity = 1; + } + + // Services μ„Ήμ…˜λ„ μžλ™ κ³„μ‚°μœΌλ‘œ λ³€κ²½ (ν•˜λ“œμ½”λ”© κ°’κ³Ό λ™μΌν•˜κ²Œ) + // ν•˜λ“œμ½”λ”© κ°’: ["200%", "100%", "0%", "-100%", "-200%"] + const servicesSpreadX = servicesCardIds.map((_, i) => { + if (i === 0) return "200%"; + if (i === 1) return "100%"; + if (i === 2) return "0%"; + if (i === 3) return "-100%"; + if (i === 4) return "-200%"; + // μΆ”κ°€ μΉ΄λ“œκ°€ μžˆμ„ 경우λ₯Ό μœ„ν•œ 일반 곡식 + const centerIndex = Math.floor(servicesCardIds.length / 2); + return `${(i - centerIndex) * 100}%`; + }); + + // ν•˜λ“œμ½”λ”© κ°’: [-8, -4, 0, 4, 8] + const servicesSpreadRotate = servicesCardIds.map((_, i) => { + if (i === 0) return -8; + if (i === 1) return -4; + if (i === 2) return 0; + if (i === 3) return 4; + if (i === 4) return 8; + // μΆ”κ°€ μΉ΄λ“œκ°€ μžˆμ„ 경우λ₯Ό μœ„ν•œ 일반 곡식 + const centerIndex = Math.floor(servicesCardIds.length / 2); + return (i - centerIndex) * 4; + }); + + let x, rotate, rotationY; + if (cardProgress < 0.6) { + x = servicesSpreadX[index]; + rotate = servicesSpreadRotate[index]; + rotationY = 0; + } else if (cardProgress < 1) { + const normalized = smoothStep((cardProgress - 0.6) / 0.4); + x = gsap.utils.interpolate(servicesSpreadX[index], "0%", normalized); + rotate = gsap.utils.interpolate(servicesSpreadRotate[index], 0, normalized); + rotationY = normalized * 180; + } else { + x = "0%"; + rotate = 0; + rotationY = 180; + } + + gsap.set(cardId, { + opacity: opacity, + y: y, + x: x, + rotate: rotate, + scale: scale, + }); + + gsap.set(innerCard, { + rotationY: rotationY, + }); + }); + }, + }); + } +}); \ No newline at end of file