diff --git a/package.json b/package.json index b173fb3..40ae7f3 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@tailwindcss/vite": "^4.3.3", + "@vis.gl/react-google-maps": "^1.9.0", "lucide-react": "^0.468.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b23bb6..81e64dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@tailwindcss/vite': specifier: ^4.3.3 version: 4.3.3(vite@6.4.3(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0)) + '@vis.gl/react-google-maps': + specifier: ^1.9.0 + version: 1.9.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) lucide-react: specifier: ^0.468.0 version: 0.468.0(react@19.2.7) @@ -287,6 +290,9 @@ packages: cpu: [x64] os: [win32] + '@googlemaps/js-api-loader@2.1.1': + resolution: {integrity: sha512-yUpAwksbHrlZIWD49JmveNSfBG4oAK0AwMknfSaPMnP5N7UT8oFRVCqwjGb1XQovi//7KLbPQKZpbofiLGzpDw==} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -308,6 +314,7 @@ packages: engines: {node: ^22.20 || ^24.12 || >=25} cpu: [x64] os: [linux] + libc: [glibc] '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} @@ -355,66 +362,79 @@ packages: resolution: {integrity: sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.62.4': resolution: {integrity: sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.62.4': resolution: {integrity: sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.62.4': resolution: {integrity: sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.62.4': resolution: {integrity: sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.62.4': resolution: {integrity: sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.62.4': resolution: {integrity: sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.62.4': resolution: {integrity: sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.62.4': resolution: {integrity: sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.62.4': resolution: {integrity: sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.62.4': resolution: {integrity: sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.62.4': resolution: {integrity: sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.62.4': resolution: {integrity: sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.62.4': resolution: {integrity: sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==} @@ -552,24 +572,28 @@ packages: engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.3.3': resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.3.3': resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.3.3': resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.3.3': resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} @@ -619,6 +643,9 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/google.maps@3.66.0': + resolution: {integrity: sha512-HOvl5nXOBazgI9rgG79mWm4sNuA08ig+AMvdCQ/rEAzXJQFfKj/LqjaP+fHLM5JFa68+VKiN15NWFBos8/Kr9w==} + '@types/node@24.13.2': resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==} @@ -630,6 +657,12 @@ packages: '@types/react@19.2.17': resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + '@vis.gl/react-google-maps@1.9.0': + resolution: {integrity: sha512-ELL/zo8mRMPGDQZwHRClf2tE1/PMYYKjg+C/dUn8rmlQ0e+s7rkcNmCgaeBAV48GJzEcrBsr+vjDv4AvzJrlUA==} + peerDependencies: + react: '>=16.8.0 || ^19.0 || ^19.0.0-rc' + react-dom: '>=16.8.0 || ^19.0 || ^19.0.0-rc' + '@vitejs/plugin-react@4.7.0': resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -721,6 +754,10 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + fast-equals@6.0.2: + resolution: {integrity: sha512-sAjhj9ZhOxYCGiNMnZLaucOqf5ZeFnHNoKoAZiD9thhJ0N8RP85qJK759/97C/3L7NzzmGVB5uiX9AUpySZmUQ==} + engines: {node: '>=6.0.0'} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -808,24 +845,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -1225,6 +1266,10 @@ snapshots: '@esbuild/win32-x64@0.25.12': optional: true + '@googlemaps/js-api-loader@2.1.1': + dependencies: + '@types/google.maps': 3.66.0 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -1493,6 +1538,8 @@ snapshots: '@types/estree@1.0.9': {} + '@types/google.maps@3.66.0': {} + '@types/node@24.13.2': dependencies: undici-types: 7.18.2 @@ -1506,6 +1553,14 @@ snapshots: dependencies: csstype: 3.2.3 + '@vis.gl/react-google-maps@1.9.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@googlemaps/js-api-loader': 2.1.1 + '@types/google.maps': 3.66.0 + fast-equals: 6.0.2 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + '@vitejs/plugin-react@4.7.0(vite@6.4.3(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0))': dependencies: '@babel/core': 7.29.7 @@ -1608,6 +1663,8 @@ snapshots: estree-walker@2.0.2: {} + fast-equals@6.0.2: {} + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: picomatch: 4.0.5 diff --git a/src/assets/icons/Questy.svg b/src/assets/icons/Questy.svg new file mode 100644 index 0000000..2e64470 --- /dev/null +++ b/src/assets/icons/Questy.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/icons/QuestyAirplane.svg b/src/assets/icons/QuestyAirplane.svg new file mode 100644 index 0000000..21de989 --- /dev/null +++ b/src/assets/icons/QuestyAirplane.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/QuestyMain.svg b/src/assets/icons/QuestyMain.svg new file mode 100644 index 0000000..d2267be --- /dev/null +++ b/src/assets/icons/QuestyMain.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/icons/star.svg b/src/assets/icons/star.svg new file mode 100644 index 0000000..e1f9af2 --- /dev/null +++ b/src/assets/icons/star.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/home/HomeHero.tsx b/src/components/home/HomeHero.tsx index c8b2666..5449fe2 100644 --- a/src/components/home/HomeHero.tsx +++ b/src/components/home/HomeHero.tsx @@ -1,50 +1,74 @@ import { ChevronRight } from "lucide-react"; -import { Button } from "@/components/UI"; type HomeHeroProps = { nickname: string; - mascotSrc: string; + questySrc: string; + airplaneSrc: string; onExploreClick: () => void; }; export default function HomeHero({ nickname, - mascotSrc, + questySrc, + airplaneSrc, onExploreClick, }: HomeHeroProps) { return ( -
-
- - {nickname}님 안녕하세요! +
+
+ + 안녕 {nickname}님! -

+

미션으로 떠나는
- 특별한 여행 + 특별한 여행

-

+

Questy와 함께 일상을
여행으로 바꿔보세요

- + +
- Quespot 캐릭터 +
+ {/* 1) 처음 가만히 떠 있는 퀘스티 */} + 퀘스티 + + {/* 2) 1~2초 뒤 들어오는 비행기 + 탑승 퀘스티 */} +
+ 퀘스티 비행기 + + 비행기를 타는 퀘스티 + + + + +
+
); } \ No newline at end of file diff --git a/src/index.css b/src/index.css index 395f3c8..c4971d8 100644 --- a/src/index.css +++ b/src/index.css @@ -3,3 +3,305 @@ @theme { --color-primary: #5bb5f8; } +.no-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; +} + +.no-scrollbar::-webkit-scrollbar { + display: none; +} +@keyframes questy-float { + 0%, + 100% { + transform: translateY(0) rotate(-1deg); + } + + 50% { + transform: translateY(-8px) rotate(2deg); + } +} + +.animate-questy-float { + animation: questy-float 3s ease-in-out infinite; +} +@keyframes questy-tumble { + 0% { + transform: translateY(0) rotate(-1deg) scale(1); + } + + 18% { + transform: translateY(-7px) rotate(2deg) scale(1.02); + } + + 36% { + transform: translateY(0) rotate(-1deg) scale(1); + } + + 48% { + transform: translateY(-10px) rotate(8deg) scale(1.04); + } + + 56% { + transform: translateY(-12px) rotate(180deg) scale(1.05); + } + + 64% { + transform: translateY(-8px) rotate(360deg) scale(1.04); + } + + 72% { + transform: translateY(0) rotate(2deg) scale(1); + } + + 86% { + transform: translateY(-6px) rotate(-2deg) scale(1.02); + } + + 100% { + transform: translateY(0) rotate(-1deg) scale(1); + } +} + +.animate-questy-tumble { + transform-origin: center center; + animation: questy-tumble 5.6s ease-in-out infinite; +} +@keyframes questy-airplane-fly { + 0% { + transform: translate(0, 0) rotate(-2deg) scale(1); + } + + 12% { + transform: translate(-4px, -6px) rotate(2deg) scale(1.02); + } + + 26% { + transform: translate(6px, 4px) rotate(-1deg) scale(1); + } + + 42% { + transform: translate(18px, -12px) rotate(5deg) scale(1.03); + } + + 56% { + transform: translate(38px, -24px) rotate(9deg) scale(1.05); + } + + 68% { + transform: translate(78px, -38px) rotate(14deg) scale(1.02); + opacity: 1; + } + + 76% { + transform: translate(128px, -54px) rotate(18deg) scale(0.94); + opacity: 0; + } + + 77% { + transform: translate(-120px, 36px) rotate(-10deg) scale(0.9); + opacity: 0; + } + + 86% { + transform: translate(-58px, 16px) rotate(-5deg) scale(0.96); + opacity: 1; + } + + 94% { + transform: translate(-12px, -4px) rotate(2deg) scale(1.02); + } + + 100% { + transform: translate(0, 0) rotate(-2deg) scale(1); + } +} + +.animate-questy-airplane-fly { + transform-origin: center center; + animation: questy-airplane-fly 6.4s ease-in-out infinite; +} + +@keyframes airplane-trail { + 0%, + 100% { + transform: translateX(0); + opacity: 0.4; + } + + 45% { + transform: translateX(-16px); + opacity: 0.9; + } + + 75% { + transform: translateX(-34px); + opacity: 0; + } +} + +.animate-airplane-trail { + animation: airplane-trail 1.4s ease-in-out infinite; +} + +.animate-airplane-trail-delay { + animation: airplane-trail 1.6s ease-in-out infinite; + animation-delay: 0.25s; +} + +.animate-airplane-trail-slow { + animation: airplane-trail 1.8s ease-in-out infinite; + animation-delay: 0.45s; +} +@keyframes questy-wait { + 0%, + 100% { + transform: translateY(0) rotate(0deg) scale(1); + opacity: 1; + } + + 15% { + transform: translateY(-4px) rotate(-2deg) scale(1.02); + opacity: 1; + } + + 30% { + transform: translateY(2px) rotate(2deg) scale(0.99); + opacity: 1; + } + + 42% { + transform: translateY(-3px) rotate(-1deg) scale(1.01); + opacity: 1; + } + + 48% { + transform: translateY(0) scale(1); + opacity: 1; + } + + 56% { + transform: translateY(-2px) scale(0.96); + opacity: 0; + } + + 100% { + transform: translateY(0) scale(0.96); + opacity: 0; + } +} + +.animate-questy-wait { + animation: questy-wait 7.2s ease-in-out infinite; +} + +@keyframes plane-enter-and-fly { + 0% { + transform: translateX(180px) translateY(18px) rotate(0deg) scale(0.95); + opacity: 0; + } + + 16% { + transform: translateX(180px) translateY(18px) rotate(0deg) scale(0.95); + opacity: 0; + } + + 28% { + transform: translateX(32px) translateY(8px) rotate(-2deg) scale(1); + opacity: 1; + } + + 40% { + transform: translateX(8px) translateY(4px) rotate(1deg) scale(1); + opacity: 1; + } + + 52% { + transform: translateX(0px) translateY(0px) rotate(0deg) scale(1); + opacity: 1; + } + + 66% { + transform: translateX(-32px) translateY(-18px) rotate(-6deg) scale(1.02); + opacity: 1; + } + + 78% { + transform: translateX(-92px) translateY(-46px) rotate(-10deg) scale(0.98); + opacity: 1; + } + + 88% { + transform: translateX(-156px) translateY(-84px) rotate(-12deg) scale(0.9); + opacity: 0; + } + + 89% { + transform: translateX(190px) translateY(28px) rotate(3deg) scale(0.92); + opacity: 0; + } + + 100% { + transform: translateX(180px) translateY(18px) rotate(0deg) scale(0.95); + opacity: 0; + } +} + +.animate-plane-enter-and-fly { + animation: plane-enter-and-fly 7.2s ease-in-out infinite; +} + +@keyframes questy-on-plane { + 0%, + 100% { + transform: translateY(0) rotate(0deg); + } + + 30% { + transform: translateY(-1px) rotate(-1deg); + } + + 60% { + transform: translateY(2px) rotate(1deg); + } + + 80% { + transform: translateY(-2px) rotate(-2deg); + } +} + +.animate-questy-on-plane { + animation: questy-on-plane 1.6s ease-in-out infinite; +} + +@keyframes air-trail { + 0%, + 100% { + opacity: 0; + transform: translateX(0) scaleX(0.9); + } + + 35% { + opacity: 0.85; + transform: translateX(-6px) scaleX(1); + } + + 80% { + opacity: 0; + transform: translateX(-24px) scaleX(1.1); + } +} + +.animate-air-trail-1 { + animation: air-trail 1s ease-in-out infinite; +} + +.animate-air-trail-2 { + animation: air-trail 1.1s ease-in-out infinite; + animation-delay: 0.1s; +} + +.animate-air-trail-3 { + animation: air-trail 1.2s ease-in-out infinite; + animation-delay: 0.2s; +} \ No newline at end of file diff --git a/src/layouts/BottomNavigationLayout.tsx b/src/layouts/BottomNavigationLayout.tsx index 49f6432..de03ede 100644 --- a/src/layouts/BottomNavigationLayout.tsx +++ b/src/layouts/BottomNavigationLayout.tsx @@ -38,7 +38,7 @@ const NAV_ITEMS = [ export default function BottomNavigationLayout() { return ( <> -
+
@@ -49,38 +49,29 @@ export default function BottomNavigationLayout() { const BottomNavigation = () => { return ( -