From 53d934a7c6191ef842a7d6716b8b2a3fde2b60ae Mon Sep 17 00:00:00 2001 From: Aviral235 Date: Thu, 12 Feb 2026 14:56:18 +0530 Subject: [PATCH] added new components --- front/package-lock.json | 35 +-- front/package.json | 26 ++- front/src/App.js | 30 ++- front/src/Components/About.jsx | 80 +++++++ front/src/Components/AnimatedTitle.jsx | 75 ++++++ front/src/Components/Button.jsx | 32 +++ front/src/Components/Contact.jsx | 53 +++++ front/src/Components/Feature.jsx | 188 +++++++++++++++ front/src/Components/Footer.jsx | 38 +++ front/src/Components/Hero.jsx | 305 +++++++++++++++++++++++++ front/src/Components/Navbar.jsx | 156 ++++++++++++- front/src/Components/Story.jsx | 113 +++++++++ front/src/Components/VideoPreview.jsx | 14 ++ 13 files changed, 1103 insertions(+), 42 deletions(-) create mode 100644 front/src/Components/About.jsx create mode 100644 front/src/Components/AnimatedTitle.jsx create mode 100644 front/src/Components/Button.jsx create mode 100644 front/src/Components/Contact.jsx create mode 100644 front/src/Components/Feature.jsx create mode 100644 front/src/Components/Footer.jsx create mode 100644 front/src/Components/Hero.jsx create mode 100644 front/src/Components/Story.jsx create mode 100644 front/src/Components/VideoPreview.jsx diff --git a/front/package-lock.json b/front/package-lock.json index 4b6f77a..fcca23b 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -7,13 +7,16 @@ "": { "name": "front", "version": "0.1.0", + "license": "ISC", "dependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^13.5.0", + "gsap": "^3.14.2", "react": "^19.2.4", "react-dom": "^19.2.4", + "react-icons": "^5.5.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, @@ -8609,6 +8612,12 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "license": "MIT" }, + "node_modules/gsap": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.14.2.tgz", + "integrity": "sha512-P8/mMxVLU7o4+55+1TCnQrPmgjPKnwkzkXOK1asnR9Jg2lna4tEY5qBJjMmAaOBDDZWtlRjBXjLa0w53G/uBLA==", + "license": "Standard 'no charge' license: https://gsap.com/standard-license." + }, "node_modules/gzip-size": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", @@ -13764,6 +13773,15 @@ "integrity": "sha512-SN/U6Ytxf1QGkw/9ve5Y+NxBbZM6Ht95tuXNMKs8EJyFa/Vy/+Co3stop3KBHARfn/giv+Lj1uUnTfOJ3moFEQ==", "license": "MIT" }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -13943,23 +13961,6 @@ "node": ">=14.0.0" } }, - "node_modules/react-scripts/node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/front/package.json b/front/package.json index a69eee6..ac2b3b5 100644 --- a/front/package.json +++ b/front/package.json @@ -2,21 +2,32 @@ "name": "front", "version": "0.1.0", "private": true, + "description": "This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).", + "license": "ISC", + "author": "", + "main": "postcss.config.js", + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, "dependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^13.5.0", + "gsap": "^3.14.2", "react": "^19.2.4", "react-dom": "^19.2.4", + "react-icons": "^5.5.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" + "devDependencies": { + "autoprefixer": "^10.4.24", + "postcss": "^8.5.6", + "tailwindcss": "^3.4.17" }, "eslintConfig": { "extends": [ @@ -35,10 +46,5 @@ "last 1 firefox version", "last 1 safari version" ] - }, - "devDependencies": { - "autoprefixer": "^10.4.24", - "postcss": "^8.5.6", - "tailwindcss": "^3.4.17" } } diff --git a/front/src/App.js b/front/src/App.js index ee5271d..8d36183 100644 --- a/front/src/App.js +++ b/front/src/App.js @@ -1,13 +1,25 @@ -import logo from './logo.svg'; -import './App.css'; -import Navbar from './Components/Navbar'; +import React from 'react' +import { BrowserRouter } from 'react-router-dom' +import Hero from './components/Hero' +import About from './components/About' +import Navbar from './components/Navbar' +import Feature from './components/Feature' +import Story from './components/Story' +import Contact from './components/Contact' +import Footer from './components/Footer' -function App() { +const App = () => { return ( -
- -
- ); +
+ + + + + + +
+
+ ) } -export default App +export default App; \ No newline at end of file diff --git a/front/src/Components/About.jsx b/front/src/Components/About.jsx new file mode 100644 index 0000000..37b4a76 --- /dev/null +++ b/front/src/Components/About.jsx @@ -0,0 +1,80 @@ +// Import GSAP animation libraries +import gsap from "gsap"; // Main GSAP animation library +import { useGSAP } from "@gsap/react"; // React hook for GSAP +import { ScrollTrigger } from "gsap/all"; // ScrollTrigger plugin for scroll-based animations + +// Import custom components +import AnimatedTitle from "./AnimatedTitle"; // Component for animating title text + +// Register the ScrollTrigger plugin with GSAP +gsap.registerPlugin(ScrollTrigger); + +/** + * About component - Displays information about the gaming platform + * Features a scroll-triggered animation that expands an image + */ +const About = () => { + // Initialize GSAP animations when component mounts + useGSAP(() => { + // Create a timeline for the clip animation + const clipAnimation = gsap.timeline({ + scrollTrigger: { + trigger: "#clip", // Element that triggers the animation + start: "center center", // Animation starts when center of element hits center of viewport + end: "+=800 center", // Animation ends 800px after start point + scrub: 0.5, // Smooth animation that follows scroll position with 0.5s delay + pin: true, // Pin the element during the animation + pinSpacing: true, // Maintain space in the document when element is pinned + }, + }); + + // Animation to expand the mask to full viewport size + clipAnimation.to(".mask-clip-path", { + width: "100vw", // Expand width to full viewport width + height: "100vh", // Expand height to full viewport height + borderRadius: 0, // Remove border radius for full rectangular shape + }); + }); + return ( + // Main container with full width and minimum height of viewport +
+ {/* Top content section with text and title */} +
+ {/* Subtitle with responsive font size */} +

+ Welcome to Zentry +

+ + {/* AnimatedTitle component with formatted HTML */} + + + {/* Description text positioned based on CSS class */} +
+

The Game of Games begins—your life, now an epic MMORPG

+

+ Zentry unites every player from countless games and platforms, both + digital and physical, into a unified Play Economy +

+
+
+ + {/* Animation container that's triggered on scroll */} +
+ {/* Container that gets animated by GSAP */} +
+ {/* Background image that expands during scroll */} + Background +
+
+
+ ); +}; + +export default About; \ No newline at end of file diff --git a/front/src/Components/AnimatedTitle.jsx b/front/src/Components/AnimatedTitle.jsx new file mode 100644 index 0000000..285ae66 --- /dev/null +++ b/front/src/Components/AnimatedTitle.jsx @@ -0,0 +1,75 @@ +// Import animation libraries and React hooks +import { gsap } from "gsap"; // Main GSAP animation library +import { useEffect, useRef } from "react"; // React hooks for lifecycle and DOM references +import { ScrollTrigger } from "gsap/ScrollTrigger"; // ScrollTrigger for scroll-based animations +import clsx from "clsx"; // Utility for conditionally joining CSS classes + +// Register the ScrollTrigger plugin with GSAP +gsap.registerPlugin(ScrollTrigger); + +/** + * AnimatedTitle component - Creates animated text that reveals as user scrolls + * + * @param {string} title - The title text, can include HTML tags (esp.
and ) + * @param {string} containerClass - Additional CSS classes for the container + */ +const AnimatedTitle = ({ title, containerClass }) => { + // Reference to the container element for GSAP animations + const containerRef = useRef(null); + + // Set up the animations when component mounts + useEffect(() => { + // Create a GSAP context for clean animation management + const ctx = gsap.context(() => { + // Create a timeline for the title animation + const titleAnimation = gsap.timeline({ + scrollTrigger: { + trigger: containerRef.current, // Element that triggers animation + start: "100 bottom", // Start when 100px of element enters bottom of viewport + end: "center bottom", // End when center of element reaches bottom of viewport + toggleActions: "play none none reverse", // Play on enter, reverse on exit + }, + }); + + // Animate each word from a 3D rotated state to normal + titleAnimation.to( + ".animated-word", // Target all words with this class + { + opacity: 1, // Fade in from transparent + transform: "translate3d(0, 0, 0) rotateY(0deg) rotateX(0deg)", // Reset 3D transform + ease: "power2.inOut", // Smooth easing for natural motion + stagger: 0.02, // Slight delay between each word (20ms) + }, + 0 // Start at the beginning of the timeline + ); + }, containerRef); + + // Clean up animations when component unmounts + return () => ctx.revert(); + }, []); + + // Note: Title should be provided by parent component + return ( + // Main container with reference for animations and combined CSS classes +
+ {/* Split the title by line breaks and render each line */} + {title.split("
").map((line, index) => ( +
+ {/* Split each line into individual words */} + {line.split(" ").map((word, idx) => ( + tags) + /> + ))} +
+ ))} +
+ ); +}; + +export default AnimatedTitle; \ No newline at end of file diff --git a/front/src/Components/Button.jsx b/front/src/Components/Button.jsx new file mode 100644 index 0000000..ee63279 --- /dev/null +++ b/front/src/Components/Button.jsx @@ -0,0 +1,32 @@ +import React from 'react' + +/** + * Button component - A customizable button with optional icons on both sides + * + * @param {string} title - The text displayed on the button + * @param {string} id - Unique identifier for the button + * @param {ReactNode} rightIcon - Optional icon component to display on the right side + * @param {ReactNode} leftIcon - Optional icon component to display on the left side + * @param {string} containerClass - Additional CSS classes for the button container + */ +const Button = ({title, id, rightIcon, leftIcon, containerClass}) => { + return ( + + ) +} + +export default Button \ No newline at end of file diff --git a/front/src/Components/Contact.jsx b/front/src/Components/Contact.jsx new file mode 100644 index 0000000..b92fc61 --- /dev/null +++ b/front/src/Components/Contact.jsx @@ -0,0 +1,53 @@ +import React from 'react' +import Button from './Button' + +const ImageClipBox= ({ src, clipClass}) => { + return ( +
+ +
+ ) +} +const Contact = () => { + return ( +
+
+ + {/* Decorative Images Left */} +
+ + +
+ {/* Decorative Images Right */} +
+ + +
+
+

JOIN ZENTRY

+

+ Let's build the
new era of
gaming together
+

+

+ We are looking for passionate gamers, developers, and creators to join our journey. Connect with us and be a part of something extraordinary! +

+
+
+
+ ) +} + +export default Contact \ No newline at end of file diff --git a/front/src/Components/Feature.jsx b/front/src/Components/Feature.jsx new file mode 100644 index 0000000..30fd08a --- /dev/null +++ b/front/src/Components/Feature.jsx @@ -0,0 +1,188 @@ +import React, { useState, useRef } from 'react' +import { TiLocationArrow } from 'react-icons/ti'; + +/** + * BentoTilt Component + * + * Creates a 3D tilt effect based on mouse movement over an element. + * This component adds an interactive hover effect similar to Apple's macOS "bento box" UI + * where elements respond to mouse position with subtle 3D rotation. + * + * @param {Object} props - Component props + * @param {ReactNode} props.children - Child elements to be rendered with the tilt effect + * @param {string} props.className - Additional CSS classes for styling + */ +const BentoTilt= ({ children , className= '' }) => { + // State to track the current transform style based on mouse position + const [transformStyle, setTransformStyle] = useState(0); + // Reference to the DOM element for calculating mouse position + const itemRef = useRef(null); + + /** + * Handles mouse movement over the element to create tilt effect + * Calculates the relative position of the mouse within the element + * and applies a 3D transform based on that position + */ + const handleMouseMove = (e) => { + if (!itemRef.current) return; + + // Get the element's position and dimensions + const {left , top , width, height} = itemRef.current.getBoundingClientRect(); + + // Calculate relative mouse position (0-1 range) + const relativeX = (e.clientX - left) / width; + const relativeY = (e.clientY -top) / height; + + // Calculate tilt angles based on mouse position + // The multiplier (7) controls the intensity of the tilt + const tiltX = (relativeY - 0.5 ) * 7; + const tiltY = (relativeX - 0.5) * -7; // Inverted for natural feel + + // Build the 3D transform with perspective and scale + const newTransform = `perspective(700px) rotateX(${tiltX}deg) rotateY(${tiltY}deg) scale3d(0.95, 0.95, 0.95)`; + setTransformStyle(newTransform); + } + + /** + * Resets the transform style when mouse leaves the element + */ + const handleMouseLeave = () => { + setTransformStyle(0); + } + + return ( +
+ {children} +
+ ) +} + +/** + * BentoCard Component + * + * A content card with video background and text overlay. + * Used within the BentoTilt component to create interactive feature tiles. + * + * @param {Object} props - Component props + * @param {string} props.src - Source URL for the background video + * @param {ReactNode} props.title - Title text/elements to display on the card + * @param {string} props.description - Optional description text + */ +const BentoCard =({src, title, description}) => { + return ( +
+ {/* Background video */} +
+ ) +} + +/** + * Feature Component + * + * A section showcasing different product features using an interactive bento grid layout. + * Displays multiple BentoCard components with video backgrounds and information about each feature. + * + * The layout uses a combination of grid and flex layouts to create a visually interesting arrangement, + * with interactive hover effects provided by the BentoTilt component. + */ +const Feature = () => { + return ( +
+
+ {/* Section header with introduction text */} +
+

Into the Metagame

+ +

+ Immerse yourself in a rich and ever-expanding universe where a vibrant array + of products converge into an interconnected overlay experience on your world. +

+
+ + {/* Main feature showcase - full-width */} + + radiant} + description="A cross-platform metagame app, turning your activities across Web2 and Web3 games into a rewarding adeventrue." + /> + + + {/* Grid layout for remaining features */} +
+ {/* Feature: zigma */} + + zigma} + description="An anime and gaming-inspired NFT collection - the IP prined for expansion." + /> + + + {/* Feature: nexus */} + + nexus} + description="A gamified social hub, adding a new dimension of play to social interaction for Web3 communities." + /> + + + {/* Feature: azul */} + + azul} + description="A cross-world AI Agent - elevating your gameplay to be more fun and productive." + /> + + + {/* "More coming soon" card */} + +
+

More coming soon!

+ +
+
+ + {/* Additional video feature with no text */} + + +
+
+
+ ) +} + +export default Feature \ No newline at end of file diff --git a/front/src/Components/Footer.jsx b/front/src/Components/Footer.jsx new file mode 100644 index 0000000..76eef54 --- /dev/null +++ b/front/src/Components/Footer.jsx @@ -0,0 +1,38 @@ +import React from 'react' +import { FaDiscord, FaTwitter, FaInstagram, FaFacebook, FaYoutube, FaLinkedin, FaTwitch } from 'react-icons/fa'; + + +const links = [{ + href:"https://discord.com", icon:}, + {href:"https://twitter.com", icon:}, + {href:"https://instagram.com", icon:}, + {href:"https://facebook.com", icon:}, + {href:"https://youtube.com", icon:}, + {href:"https://linkedin.com", icon:}, + {href:"https://twitch.com", icon:}, + +] +const Footer = () => { + return ( + + ) +} + +export default Footer \ No newline at end of file diff --git a/front/src/Components/Hero.jsx b/front/src/Components/Hero.jsx new file mode 100644 index 0000000..a726498 --- /dev/null +++ b/front/src/Components/Hero.jsx @@ -0,0 +1,305 @@ +import gsap from 'gsap'; +import { useGSAP } from '@gsap/react'; +import {ScrollTrigger} from "gsap/all"; +import { TiLocationArrow } from 'react-icons/ti'; +import { useState, useRef, useEffect } from 'react'; +// import VideoPreview from './VideoPreview'; +import Button from './Button'; + +// Register ScrollTrigger plugin with GSAP +gsap.registerPlugin(ScrollTrigger); + +/** + * Hero Component + * + * An immersive hero section featuring interactive video transitions and 3D effects. + * This component displays a fullscreen video with interactive elements, transitions between + * multiple videos, and provides visual loading feedback while videos are loading. + */ +const Hero = () => { + // State to track current video index (1-4) + const [currentIndex, setCurrentIndex] = useState(1); + // State to track if user has clicked to trigger video transition + const [hasClicked, setHasClicked] = useState(false); + + // Loading state management for videos + const [loading, setLoading] = useState(true); + const [loadedVideos, setLoadedVideos] = useState(0); + const totalVideos = 4; + + // Refs for DOM element access + const nextVdRef = useRef(null); + const miniVideoRef = useRef(null); + + /** + * Increment loaded videos counter whenever a video loads + * Used to track loading progress + */ + const handleVideoLoad = () => { + setLoadedVideos((prev) => prev + 1); + }; + + /** + * Set loading state to false when all videos except one have loaded + * This ensures the UI becomes interactive fairly quickly + */ + useEffect(() => { + if (loadedVideos === totalVideos - 1) { + setLoading(false); + } + }, [loadedVideos]); + + /** + * Handle click on the mini video element + * Triggers transition animation between videos + */ + const handleMiniVdClick = () => { + setHasClicked(true); + + // Cycle to next video index + setCurrentIndex((prevIndex) => (prevIndex % totalVideos) + 1); + + const miniVideo = miniVideoRef.current; + const miniRect = miniVideo.getBoundingClientRect(); + + const videoElement = miniVideo.querySelector('video'); + if(!videoElement) return; + + // Create GSAP animation timeline + const tl = gsap.timeline(); + + // First, position the next video exactly over the mini video + tl.set("#next-video-container", { + visibility: "visible", + width: miniRect.width, + height: miniRect.height, + x: miniRect.left, + y: miniRect.top, + borderRadius: "12px", + overflow: "hidden" + }); + + // Then animate it to fill the screen + tl.to("#next-video-container", { + width: "100vw", + height: "100vh", + x: 0, + y: 0, + borderRadius: 0, + duration: 1, + ease: "power2.inOut" + }); + + // Fade out the original mini video + tl.to(miniVideo, { + opacity: 0, + duration: 0.5 + }, 0); + + // Fade in any content that should appear after the transition + tl.to(".hero-content", { + opacity: 1, + y: 0, + duration: 0.7, + stagger: 0.1, + ease: "power2.out" + }, "-=0.3"); + }; + + /** + * 3D tilt effect on mouse move + * Creates an interactive, depth-based response when hovering over the mini video + */ + const handleMouseMove = (e) => { + if (!miniVideoRef.current) return; + + // Calculate relative mouse position within the element + const { left, top, width, height } = miniVideoRef.current.getBoundingClientRect(); + const x = (e.clientX - left) / width; + const y = (e.clientY - top) / height; + + // Calculate tilt angles based on mouse position + const tiltX = (y - 0.5) * 30; // Tilt range: -15 to 15 degrees + const tiltY = (0.5 - x) * 30; // Tilt range: -15 to 15 degrees + + // Apply 3D transform with perspective + miniVideoRef.current.style.transform = `perspective(1000px) rotateX(${tiltX}deg) rotateY(${tiltY}deg) scale(1.05)`; + }; + + /** + * Reset transform when mouse leaves the element + */ + const handleMouseLeave = () => { + if (miniVideoRef.current) { + miniVideoRef.current.style.transform = 'perspective(1000px) rotateX(0deg) rotateY(0deg) scale(0.5)'; + } + }; + + /** + * GSAP animation for video transitions + * Triggered when currentIndex changes (user clicks to switch videos) + */ + useGSAP( + () => { + if (hasClicked) { + // Make the next video visible + gsap.set("#next-video", { visibility: "visible" }); + + // Scale up the next video to fill the screen + gsap.to("#next-video", { + transformOrigin: "center center", + scale: 1, + width: "100%", + height: "100%", + duration: 1, + ease: "power1.inOut", + onStart: () => nextVdRef.current.play(), // Start playing the video + }); + + // Animate the current video scaling for transition effect + gsap.from("#current-video", { + transformOrigin: "center center", + scale: 0, + duration: 1.5, + ease: "power1.inOut", + }); + } + }, + { + dependencies: [currentIndex], + revertOnUpdate: true, // Revert animations when dependencies change + } + ); + + /** + * GSAP animation for the video frame clip-path effect + * Creates a dynamic shape for the hero section that responds to scrolling + */ + useGSAP(() => { + // Set initial clip-path and border radius + gsap.set("#video-frame", { + clipPath: "polygon(14% 0, 72% 0, 88% 90%, 0 95%)", + borderRadius: "0% 0% 50% 10%", + }); + + // Animate the clip-path on scroll + gsap.from("#video-frame", { + clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", + borderRadius: "0% 0% 0% 0%", + ease: "power1.inOut", + scrollTrigger: { + trigger: "#video-frame", + start: "center center", + end: "bottom center", + scrub: true, // Smooth animation tied to scroll position + }, + }); + }); + + /** + * Helper function to get video source path based on index + */ + const getVideoSrc = (index) => `/videos/hero-${index}.mp4`; + + return ( +
+ {/* Loading overlay - displays while videos are loading */} + {loading && ( +
+ {/* Three-body loading animation */} +
+
+
+
+
+
+ )} + + {/* Main video container with clip-path effect */} +
+
+ {/* Interactive mini video that triggers transitions */} +
+
+
+
+ + {/* Next video element - initially hidden, shown during transition */} +
+ + {/* Bottom-right "GAMING" text */} +

+ GAMING +

+ + {/* Hero content overlay with heading, text and button */} +
+
+ {/* "REDEFINE" heading with styled letter */} +

+ redefine +

+ + {/* Tagline text */} +

+ Enter the Metagame Layer
Unleash the Play Economy +

+ + {/* CTA Button */} +
+
+
+ + {/* Duplicate "GAMING" text with different styling outside the main container */} +

+ GAMING +

+
+ ); +}; + +export default Hero; \ No newline at end of file diff --git a/front/src/Components/Navbar.jsx b/front/src/Components/Navbar.jsx index d3a8b8f..749b21d 100644 --- a/front/src/Components/Navbar.jsx +++ b/front/src/Components/Navbar.jsx @@ -1,10 +1,154 @@ -// A simple Navbar Component -import React from 'react' +import React, { useEffect } from 'react' +import { useRef, useState } from 'react'; +import Button from './Button'; +import { TiLocationArrow } from 'react-icons/ti'; +import { useWindowScroll } from 'react-use'; +import gsap from 'gsap'; +/** + * Navigation items for the main menu + * Each item will be rendered as a link with the same text as its anchor + */ +const NAV_ITEMS = [ + 'Nexus', 'Vault', 'Prolugue', 'About', 'Contact']; + +/** + * Navbar Component + * + * A responsive navigation bar with: + * - Floating effect when scrolling + * - Audio control button with animated indicators + * - Transparent background at top, glassy black when scrolled + * - Product button and navigation links + * + * The navbar visibility is controlled by scroll direction - hides when scrolling down, + * shows when scrolling up or at the top of the page. + */ const Navbar = () => { - return ( -
Navbar
- ) + // State for audio player control + const [isAudioPlaying, setIsAudioPlaying] = useState(false); + const [isIndicatorActive, setIsIndicatorActive] = useState(false); + + // Ref for navbar container element + const navContainerRef = useRef(null); + // Ref for audio element + const audioElementRef = useRef(null); + + // Track scroll position and navbar visibility + const [lastScrollY, setLastScrollY] = useState(0); + const [isNavVisible, setIsNavVisible] = useState(true); + + // Get current scroll position using useWindowScroll hook + const { y:currentScrollY } = useWindowScroll(); + + /** + * Effect to handle scrolling behavior and add/remove floating navbar classes + * - Shows navbar at top of page (currentScrollY === 0) + * - Hides navbar when scrolling down + * - Shows navbar when scrolling up + * - Adds floating-nav class when not at the top + */ + useEffect(() => { + const navContainer = document.querySelector('header'); + const navElement = document.querySelector('nav'); + if (!navContainerRef.current || !navContainer || !navElement) return; + + if (currentScrollY === 0) { + setIsNavVisible(true); + navElement.classList.remove('floating-nav'); + } else if(currentScrollY > lastScrollY) { + setIsNavVisible(false); + navElement.classList.add('floating-nav'); + } else if(currentScrollY < lastScrollY) { + setIsNavVisible(true); + navElement.classList.add('floating-nav'); + } + setLastScrollY(currentScrollY); + }, [currentScrollY, lastScrollY]); + + /** + * Effect to animate navbar visibility using GSAP + * Animates the Y position and opacity based on isNavVisible state + */ + useEffect(() => { + gsap.to(navContainerRef.current, { + y: isNavVisible ? 0 : -100, + opacity: isNavVisible ? 1 : 0, + duration: 0.3, + ease: "power2.out" + }) + }, [isNavVisible]); + + /** + * Toggle audio playback and indicator animation state + */ + const toggleAudioIndicator = () => { + setIsAudioPlaying((prev) => !prev); + setIsIndicatorActive((prev) => !prev); + } + + /** + * Effect to control audio playback based on isAudioPlaying state + */ + useEffect(() => { + if(isAudioPlaying){ + audioElementRef.current.play(); + } + else{ + audioElementRef.current.pause(); + } + }, [isAudioPlaying]); + + return ( +
+ +
+ ) } -export default Navbar \ No newline at end of file +export default Navbar; \ No newline at end of file diff --git a/front/src/Components/Story.jsx b/front/src/Components/Story.jsx new file mode 100644 index 0000000..eb1417d --- /dev/null +++ b/front/src/Components/Story.jsx @@ -0,0 +1,113 @@ +// Import necessary React libraries and components +import React, { useRef} from 'react' +import AnimatedTitle from './AnimatedTitle.jsx' // Import custom animated title component +import gsap from 'gsap'; // Import GSAP for animations +import Button from './Button.jsx'; // Import custom button component + +// Story component definition +const Story = () => { + // Create a reference for the image element to apply 3D effects + const frameRef = useRef(null); + + // Handler for mouse leaving the image - resets the 3D rotation effect + const handleMouseLeave = () => { + const element = frameRef.current; // Get the referenced DOM element + gsap.to(element, { + duration: 0.5, // Animation takes 0.5 seconds + rotationX: 0, // Reset X rotation to 0 + rotationY: 0, // Reset Y rotation to 0 + ease: "power2.inOut", // Use power2 easing for smooth animation + }) + } + + // Handler for mouse movement over the image - creates 3D tilt effect + const handleMouseMove = (e) => { + const { clientX, clientY } = e; // Get mouse position from event + const element = frameRef.current; // Get the referenced DOM element + if(!element) return; // Safety check if element doesn't exist + + // Calculate the position of the mouse relative to the element + const rect = element.getBoundingClientRect(); + const x = clientX - rect.left; + const y = clientY - rect.top; + + // Find the center point of the element + const centerX = rect.width / 2; + const centerY = rect.height / 2; + + // Calculate rotation values based on mouse position relative to center + // Negative for rotateX creates a realistic "looking at" effect + const rotateX = (y - centerY) / centerY * -10; // Scale by 10 degrees + const rotateY = (x - centerX) / centerX * 10; // Scale by 10 degrees + + // Apply 3D rotation effect using GSAP + gsap.to(element, { + duration: 0.3, // Quick animation for responsive feel + rotationX: rotateX, // Apply calculated X rotation + rotationY: rotateY, // Apply calculated Y rotation + transformPerspective: 500, // Set perspective for 3D effect + ease: "power2.inOut", // Use power2 easing for smooth movement + }) + } + + return ( + // Main section container with full viewport height, black background and light blue text +
+ {/* Flex container for content with full size and centered alignment */} +
+ {/* Subtitle with different text sizes based on viewport */} +

The multiversal ip World

+ + {/* Container for the title and image with relative positioning */} +
+ {/* AnimatedTitle component with HTML formatting and special styling */} + for special styling + sectionId="#story" // Connects to the section ID + containerClass="mt-5 pointer-events-none mix-blend-difference relative z-10" // Styling classes + /> + + {/* Container for story image with custom styling from index.css */} +
+ {/* Mask div with clip-path for custom shape from index.css */} +
+ {/* Content container for positioning the image */} +
+ {/* Image with 3D hover effect */} + entrance +
+
+
+
+ + {/* Container for the text content and button, positioned below the image */} +
+ {/* Inner container for text and button with responsive alignment */} +
+ {/* Descriptive paragraph with responsive text alignment */} +

+ Where realms converge, lies Zentry and the boundless pillar. Discover its secrets and shape your fate amidst infinite opportunities. +

+ {/* Call-to-action button */} +
+
+
+
+ ) +} + +export default Story \ No newline at end of file diff --git a/front/src/Components/VideoPreview.jsx b/front/src/Components/VideoPreview.jsx new file mode 100644 index 0000000..14773ed --- /dev/null +++ b/front/src/Components/VideoPreview.jsx @@ -0,0 +1,14 @@ +import React from 'react'; + +/** + + * + * @param {Object} props - Component props + * @param {ReactNode} props.children - Child elements to be rendered within the component + * @returns {ReactNode} The children passed to this component + */ +const VideoPreview = ({ children }) => { + return <>{children}; +}; + +export default VideoPreview; \ No newline at end of file