Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions front/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

80 changes: 80 additions & 0 deletions front/src/Components/About.jsx
Original file line number Diff line number Diff line change
@@ -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
<div id="about" className="min-h-screen w-screen">
{/* Top content section with text and title */}
<div className="relative mb-8 mt-36 flex flex-col items-center gap-5">
{/* Subtitle with responsive font size */}
<p className="font-general text-sm uppercase md:text-[10px]">
Welcome to Zentry
</p>

{/* AnimatedTitle component with formatted HTML */}
<AnimatedTitle
title="Disc<b>o</b>ver the world's <br /> largest shared <b>a</b>dventure" // Special letters 'o' and 'a' are bold
containerClass="mt-5 !text-black text-center" // Forced black text with center alignment
/>

{/* Description text positioned based on CSS class */}
<div className="about-subtext">
<p>The Game of Games begins—your life, now an epic MMORPG</p>
<p className="text-gray-500">
Zentry unites every player from countless games and platforms, both
digital and physical, into a unified Play Economy
</p>
</div>
</div>

{/* Animation container that's triggered on scroll */}
<div className="h-dvh w-screen" id="clip">
{/* Container that gets animated by GSAP */}
<div className="mask-clip-path about-image">
{/* Background image that expands during scroll */}
<img
src="img/about.webp"
alt="Background"
className="absolute left-0 top-0 size-full object-cover" // Positioned to fill container
/>
</div>
</div>
</div>
);
};

export default About;
75 changes: 75 additions & 0 deletions front/src/Components/AnimatedTitle.jsx
Original file line number Diff line number Diff line change
@@ -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. <br /> and <b>)
* @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
<div ref={containerRef} className={clsx("animated-title", containerClass)}>
{/* Split the title by line breaks and render each line */}
{title.split("<br />").map((line, index) => (
<div
key={index}
className="flex-center max-w-full flex-wrap gap-2 px-10 md:gap-3" // Flexbox center with responsive spacing
>
{/* Split each line into individual words */}
{line.split(" ").map((word, idx) => (
<span
key={idx}
className="animated-word text-3xl sm:text-4xl md:text-5xl lg:text-6xl" // Responsive font sizes
dangerouslySetInnerHTML={{ __html: word }} // Allow HTML in words (for <b> tags)
/>
))}
</div>
))}
</div>
);
};

export default AnimatedTitle;
32 changes: 32 additions & 0 deletions front/src/Components/Button.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<button
id={id}
className={`group relative z-50 w-fit cursor-pointer overflow-hidden bg-white rounded-full px-7 py-3 text-black ${containerClass}`}
>
{/* Left icon if provided */}
{leftIcon}

{/* Button text with custom styling */}
<span className="relative inline-flex overflow-hidden font-general text-xs uppercase">
<div>{title}</div>
</span>

{/* Right icon if provided */}
{rightIcon}
</button>
)
}

export default Button
53 changes: 53 additions & 0 deletions front/src/Components/Contact.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react'
import Button from './Button'

const ImageClipBox= ({ src, clipClass}) => {
return (
<div className={clipClass}>
<img src={src}/>
</div>
)
}
const Contact = () => {
return (
<div id='contact' className='relative my-24 min-h-[32rem] w-full px-4 md:px-16 flex items-center justify-center'>
<div className='relative rounded-3xl bg-gradient-to-br from-[#0f172a] via-[#1e293b] to-[#334155] shadow-2xl py-20 md:py-28 px-4 md:px-16 text-blue-50 overflow-hidden w-full max-w-6xl'>

{/* Decorative Images Left */}
<div className='absolute -left-16 top-0 hidden h-full w-60 md:w-72 lg:w-96 sm:block z-10'>
<ImageClipBox
clipClass="contact-clip-path-1 drop-shadow-2xl opacity-80"
src="img/contact-1.webp"
/>
<ImageClipBox
clipClass="contact-clip-path-2 lg:translate-y-40 translate-y-60 drop-shadow-xl opacity-70"
src="img/contact-2.webp"
/>
</div>
{/* Decorative Images Right */}
<div className='absolute -top-32 right-0 md:right-10 lg:top-10 w-40 md:w-60 lg:w-80 z-10'>
<ImageClipBox
src="img/swordman-partial.webp"
clipClass="absolute md:scale-125 opacity-80 rotate-6"
/>
<ImageClipBox
src="img/swordman.webp"
clipClass="sword-man-clip-path md:scale opacity-90 -rotate-3"
/>
</div>
<div className='relative z-20 flex flex-col items-center text-center uppercase'>
<p className='font-general text-xs md:text-sm tracking-widest text-blue-200 mb-2 animate-fade-in'>JOIN ZENTRY</p>
<h2 className='mt-6 mb-8 w-full font-robert-medium text-4xl md:text-6xl lg:text-7xl leading-tight md:leading-[1.1] bg-gradient-to-r from-blue-300 via-blue-100 to-blue-400 bg-clip-text text-transparent drop-shadow-lg animate-slide-up'>
<b>Let's build the <br className='hidden md:block'/> new era of <br className='hidden md:block'/> gaming together</b>
</h2>
<p className='max-w-xl mx-auto text-blue-200 text-base md:text-lg font-general mb-8 animate-fade-in-slow'>
We are looking for passionate gamers, developers, and creators to join our journey. Connect with us and be a part of something extraordinary!
</p>
<Button title="Contact Us" containerClass="mt-4 md:mt-8 px-8 py-3 rounded-full bg-gradient-to-r from-blue-500 to-blue-700 hover:from-blue-600 hover:to-blue-800 shadow-lg transition-all duration-300 cursor-pointer text-lg font-robert-medium uppercase tracking-wider animate-pop"/>
</div>
</div>
</div>
)
}

export default Contact
Loading