UNPKG

@anishpras/create-portfolio

Version:

🏗 Personal CLI to bootstrap new portfolio from the given templates.

317 lines (295 loc) 9.34 kB
import { useEffect, useState, useRef } from "react"; import { data } from "../data"; import Head from "next/head"; import IntroOverlay from "../components/introOverlay"; import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/dist/ScrollTrigger"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChevronDown } from "@fortawesome/free-solid-svg-icons"; import { motion } from "framer-motion"; import Link from "next/link"; export default function Home({ project }) { const [animationComplete, setAnimationComplete] = useState(false); const projectsRef = useRef(null); const completeAnimation = () => { setAnimationComplete(true); document.body.style.overflowY = "auto"; }; const executeScroll = () => projectsRef.current.scrollIntoView(); const scrollToProject = () => { if (typeof window !== "undefined") { if (project) { // Use the hash to find the first element with that id const element = document.getElementById(project); if (element) { // Smooth scroll to that elment element.scrollIntoView(); } } } }; useEffect(() => { // Inner Page height for mobile devices let vh = window.innerHeight * 0.01; document.documentElement.style.setProperty("--vh", `${vh}px`); // GSAP animation gsap.registerPlugin(ScrollTrigger); let tl = gsap.timeline(); let projects = gsap.utils.toArray(".project"); let mediaQuery = window.matchMedia("(min-width: 967px)"); const homeAnimation = (animation) => { if (!project) { tl.to(".ball", { duration: 2, y: "100vh", ease: "bounce.out", }) .to(".ball", { duration: 1, scale: 30, ease: "power3.out", onComplete: animation, }) .from(".after-animation", { duration: 0.8, opacity: 0, ease: "power3.out", }) .from(".title", { duration: 0.5, y: 100, delay: 0.2, opacity: 0, ease: "power3.out", }) .from(".peep-image", { duration: 0.5, y: 100, opacity: 0, ease: "power3.out", }) .from(".job-title", { duration: 0.5, y: 100, opacity: 0, ease: "power3.out", }) .from(".scroll-indicator", { duration: 0.5, y: 100, opacity: 0, ease: "power3.out", }); } else { completeAnimation(); scrollToProject(); } if (mediaQuery.matches) { projects.forEach((project) => { let tlProject = gsap.timeline({ scrollTrigger: { trigger: project, start: "top center", end: "center center", scrub: 1, }, }); let projectImage = project.querySelector("img"); let projectInfo = project.querySelector(".project-info"); tlProject .from(projectImage, { x: -300, opacity: 0, }) .from(projectInfo, { x: 300, opacity: 0, }); }); } else { projects.forEach((project) => { let tlProject = gsap.timeline({ scrollTrigger: { trigger: project, start: "top center", end: "center center", scrub: 1, }, }); let projectImage = project.querySelector("img"); let projectInfo = project.querySelector(".project-info"); tlProject .from(projectImage, { y: 100, opacity: 0, }) .from(projectInfo, { y: 100, opacity: 0, }); }); } let tlFooter = gsap.timeline({ scrollTrigger: { trigger: "footer", start: "top center", end: "top top", scrub: 1, }, }); tlFooter .from("footer h2", { y: 100, opacity: 0, duration: 0.6, }) .from("footer .footer-links", { y: 100, opacity: 0, duration: 0.6, }); }; homeAnimation(completeAnimation); }, []); return ( <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="container"> <Head> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta charSet="utf-8" /> <meta name="description" content={data.seo.description}></meta> <meta property="og:url" content={data.seo.url} /> <meta property="og:type" content="website" /> <meta property="og:locale" content="en" /> <meta property="og:image" content={data.seo.image} /> <title>{data.seo.title}</title> <link rel="icon" href={data.seo.favicon} /> </Head> {animationComplete === false && <IntroOverlay />} <div className="after-animation"> <header> <nav className="home-nav"> <div className="space-between"> <Link href="/"> <div className="logo">{data.initial}</div> </Link> <ul className="nav-list"> <li> <motion.a href={data.resumeUrl} target="_blank" rel="noopener noreferrer" whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} title="Download Resume"> Resume </motion.a> </li> </ul> </div> </nav> </header> <main className="main-home"> <div className="cta"> <h1 className="title"> I create<span className="playful"> playful </span> experiences. </h1> <img src="/images/My_Peep.png" alt="My Peep" className="peep-image" /> </div> <p className="job-title"> <span className="text-reveal"> {`${data.name} / ${data.position}`} </span> </p> <button className="scroll-indicator" onClick={executeScroll}> <span>Projects</span> <FontAwesomeIcon icon={faChevronDown} /> </button> </main> <div className="project-container" ref={projectsRef}> {data.projectsList.map( ({ name, description, image, mobileImage, link, project, GitHub, tools, index, }) => ( <div className="project" key={index} id={project}> <picture> <source srcSet={image} media="(min-width: 1280px)" /> <img className="project-image" src={mobileImage} alt={name} /> </picture> <div className="project-info"> <h2>{name}</h2> {description.split("\n").map((str, index) => ( <p key={index}>{str}</p> ))} <h3>Tools used:</h3> <ul className="tools-list"> {tools.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> <motion.div className="project-btns"> <motion.a href={link} target="_blank" rel="noopener noreferrer" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} title={`Open site of ${name}`} className="project-btn"> Open Site </motion.a> {GitHub && ( <motion.a href={GitHub} target="_blank" rel="noopener noreferrer" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} title={`View Code for ${name}`} className="project-btn"> View Code </motion.a> )} </motion.div> </div> </div> ) )} </div> <footer> <h2>Connect with Me</h2> <ul className="footer-links"> <li> <motion.a href={data.resumeUrl} target="_blank" rel="noopener noreferrer" whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} title="Download Resume"> Resume </motion.a> </li> </ul> </footer> </div> </motion.div> ); }