diff --git a/front/package-lock.json b/front/package-lock.json index 8e3edc4..abf2563 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -8,14 +8,17 @@ "name": "gamingweb", "version": "0.0.0", "license": "ISC", + "license": "ISC", "dependencies": { "@gsap/react": "^2.1.2", "@tailwindcss/vite": "^4.1.8", "clsx": "^2.1.1", "gsap": "^3.13.0", + "gsap": "^3.14.2", "react": "^19.1.0", "react-dom": "^19.1.0", "react-icons": "^5.5.0", + "react-icons": "^5.5.0", "react-router-dom": "^7.6.2", "react-use": "^17.6.0" }, 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/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