├── .eslintrc.json ├── next.config.js ├── postcss.config.js ├── src ├── app │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ └── page.tsx ├── components │ ├── landing │ │ ├── animation.js │ │ └── Landing.tsx │ ├── preloader │ │ ├── animation.js │ │ └── Preloader.tsx │ ├── description │ │ ├── animation.js │ │ └── Description.tsx │ ├── header │ │ ├── anim.js │ │ ├── nav │ │ │ ├── navLink │ │ │ │ └── NavLink.tsx │ │ │ ├── curve │ │ │ │ └── Curve.tsx │ │ │ └── Nav.tsx │ │ └── Header.tsx │ ├── projects │ │ ├── projectComponents │ │ │ ├── project │ │ │ │ └── Project.tsx │ │ │ └── modal │ │ │ │ └── Modal.tsx │ │ └── Projects.tsx │ ├── slidingImages │ │ └── SlidingImages.tsx │ └── contact │ │ └── Contact.tsx └── common │ ├── magnetic │ └── Magnetic.jsx │ └── button │ └── Button.jsx ├── public ├── images │ ├── c2.jpg │ ├── wix.jpg │ ├── funny.jpg │ ├── google.jpg │ ├── maven.jpg │ ├── panda.jpg │ ├── powell.jpg │ ├── decimal.jpg │ ├── silencio.png │ ├── background.jpg │ ├── background2.jpg │ ├── c2montreal.png │ ├── locomotive.png │ └── officestudio.png ├── vercel.svg └── next.svg ├── .gitignore ├── tailwind.config.ts ├── tsconfig.json ├── package.json └── README.md /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/src/app/favicon.ico -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | cursor: wait; 7 | } -------------------------------------------------------------------------------- /public/images/c2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/c2.jpg -------------------------------------------------------------------------------- /public/images/wix.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/wix.jpg -------------------------------------------------------------------------------- /public/images/funny.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/funny.jpg -------------------------------------------------------------------------------- /public/images/google.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/google.jpg -------------------------------------------------------------------------------- /public/images/maven.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/maven.jpg -------------------------------------------------------------------------------- /public/images/panda.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/panda.jpg -------------------------------------------------------------------------------- /public/images/powell.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/powell.jpg -------------------------------------------------------------------------------- /public/images/decimal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/decimal.jpg -------------------------------------------------------------------------------- /public/images/silencio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/silencio.png -------------------------------------------------------------------------------- /public/images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/background.jpg -------------------------------------------------------------------------------- /public/images/background2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/background2.jpg -------------------------------------------------------------------------------- /public/images/c2montreal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/c2montreal.png -------------------------------------------------------------------------------- /public/images/locomotive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/locomotive.png -------------------------------------------------------------------------------- /public/images/officestudio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PaAmatJow/awwwards-landing-page-next-framermotion-gsap/HEAD/public/images/officestudio.png -------------------------------------------------------------------------------- /src/components/landing/animation.js: -------------------------------------------------------------------------------- 1 | export const slideUp = { 2 | initial: { 3 | y: 300, 4 | }, 5 | enter: { 6 | y: 0, 7 | transition: { 8 | duration: 0.6, ease: [0.33, 1, 0.68, 1], delay: 2.5 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/components/preloader/animation.js: -------------------------------------------------------------------------------- 1 | export const slideUp = { 2 | initial: { 3 | top: 0, 4 | }, 5 | exit: { 6 | top: '-100vh', 7 | transition: { duration: 0.8, ease: [0.76, 0, 0.24, 1], delay: 0.2 }, 8 | }, 9 | }; 10 | 11 | export const opacity = { 12 | initial: { 13 | opacity: 0, 14 | }, 15 | enter: { 16 | opacity: 0.75, 17 | transition: { duration: 1, delay: 0.2 }, 18 | }, 19 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss'; 2 | 3 | const config: Config = { 4 | content: [ 5 | './src/pages/**/*.{js,ts,jsx,tsx,mdx}', 6 | './src/components/**/*.{js,ts,jsx,tsx,mdx}', 7 | './src/app/**/*.{js,ts,jsx,tsx,mdx}', 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 13 | 'gradient-conic': 14 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | }; 20 | export default config; 21 | -------------------------------------------------------------------------------- /src/components/description/animation.js: -------------------------------------------------------------------------------- 1 | export const slideUp = { 2 | initial: { 3 | y: '100%' 4 | }, 5 | open: (i) => ({ 6 | y: '0%', 7 | transition: {duration: 0.5, delay: 0.01 * i} 8 | }), 9 | closed: { 10 | y: '100%', 11 | transition: { 12 | duration: 0.5 13 | } 14 | } 15 | } 16 | 17 | export const opacity = { 18 | initial: { 19 | opacity: 0, 20 | y: 100 21 | }, 22 | open: { 23 | opacity: 1, 24 | y: 0, 25 | transition: {duration: 0.5} 26 | }, 27 | closed: { 28 | opacity: 0, 29 | transition: {duration: 0.5} 30 | } 31 | } -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import { Inter } from 'next/font/google' 3 | import './globals.css' 4 | import React from 'react' 5 | import Header from '@/components/header/Header' 6 | 7 | const inter = Inter({ subsets: ['latin'] }) 8 | 9 | export const metadata: Metadata = { 10 | title: 'Create Next App', 11 | description: 'Generated by create next app', 12 | } 13 | 14 | export default function RootLayout({ 15 | children, 16 | }: { 17 | children: React.ReactNode 18 | }) { 19 | return ( 20 | 21 | 22 |
23 | {children} 24 | 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/common/button/Button.jsx"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /src/components/header/anim.js: -------------------------------------------------------------------------------- 1 | export const menuSlide = { 2 | initial: { 3 | x: 'calc(100% + 100px)', 4 | }, 5 | enter: { 6 | x: '0%', 7 | transition: { 8 | duration: 0.8, 9 | ease: [0.76, 0, 0.24, 1], 10 | }, 11 | }, 12 | exit: { 13 | x: 'calc(100% + 100px)', 14 | transition: { 15 | duration: 0.8, 16 | ease: [0.76, 0, 0.24, 1], 17 | }, 18 | }, 19 | }; 20 | 21 | export const slide = { 22 | initial: { 23 | x: '80px', 24 | }, 25 | enter: (i) => ({ 26 | x: '0px', 27 | transition: { 28 | duration: 0.8, 29 | ease: [0.76, 0, 0.24, 1], 30 | delay: 0.05 * i 31 | }, 32 | }), 33 | exit: (i) => ({ 34 | x: '80px', 35 | transition: { 36 | duration: 0.8, 37 | ease: [0.76, 0, 0.24, 1], 38 | delay: 0.05 * i 39 | }, 40 | }), 41 | }; -------------------------------------------------------------------------------- /src/components/header/nav/navLink/NavLink.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { motion } from 'framer-motion'; 3 | import { slide } from '../../anim.js'; 4 | 5 | interface LinkData { 6 | index: number; 7 | title: string; 8 | href: string; 9 | } 10 | 11 | interface NavLinkProps { 12 | data: LinkData; 13 | setIsActive: React.Dispatch>; 14 | } 15 | 16 | 17 | const NavLink = ({data, setIsActive}: NavLinkProps) => { 18 | return ( 19 | 20 |
21 | {setIsActive(false)}}> 22 | {data.title} 23 | 24 |
25 | ) 26 | } 27 | export default NavLink -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awwwards-landing-page-next-framermotion-gsap", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "framer-motion": "^10.17.0", 13 | "gsap": "^3.12.4", 14 | "locomotive-scroll": "^5.0.0-beta.11", 15 | "next": "14.0.4", 16 | "react": "^18", 17 | "react-dom": "^18" 18 | }, 19 | "devDependencies": { 20 | "@types/locomotive-scroll": "^4.1.3", 21 | "@types/node": "^20", 22 | "@types/react": "^18", 23 | "@types/react-dom": "^18", 24 | "autoprefixer": "^10.0.1", 25 | "eslint": "^8", 26 | "eslint-config-next": "14.0.4", 27 | "postcss": "^8", 28 | "tailwindcss": "^3.3.0", 29 | "typescript": "^5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/components/header/nav/curve/Curve.tsx: -------------------------------------------------------------------------------- 1 | import {motion} from 'framer-motion' 2 | 3 | const Curve = () => { 4 | 5 | const initialPath = `M100 0 L200 0 L200 ${window.innerHeight} L100 ${ 6 | window.innerHeight 7 | } Q-100 ${window.innerHeight / 2} 100 0`; 8 | const targetPath = `M100 0 L200 0 L200 ${window.innerHeight} L100 ${ 9 | window.innerHeight 10 | } Q100 ${window.innerHeight / 2} 100 0`; 11 | 12 | const curve = { 13 | initial: { 14 | d: initialPath, 15 | }, 16 | enter: { 17 | d: targetPath, 18 | transition: { duration: 1, ease: [0.76, 0, 0.24, 1] }, 19 | }, 20 | exit: { 21 | d: initialPath, 22 | transition: { duration: 1, ease: [0.76, 0, 0.24, 1] }, 23 | }, 24 | }; 25 | 26 | return ( 27 | 28 | 34 | 35 | ); 36 | } 37 | export default Curve -------------------------------------------------------------------------------- /src/components/projects/projectComponents/project/Project.tsx: -------------------------------------------------------------------------------- 1 | interface ProjectProps { 2 | index: number; 3 | title: string; 4 | setModal: (value: { active: boolean; index: number }) => void; 5 | } 6 | 7 | const Project = ({ index, title, setModal }: ProjectProps) => { 8 | return ( 9 |
{ 12 | setModal({ active: true, index: index }); 13 | }} 14 | onMouseLeave={() => { 15 | setModal({ active: false, index: index }); 16 | }} 17 | > 18 |

19 | {title} 20 |

21 |

22 | Design & Development 23 |

24 |
25 | ); 26 | }; 27 | export default Project; 28 | -------------------------------------------------------------------------------- /src/common/magnetic/Magnetic.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import gsap from 'gsap'; 3 | 4 | const Magnetic = ({children}) => { 5 | const magnetic = useRef(null); 6 | 7 | useEffect(() => { 8 | console.log(children); 9 | const xTo = gsap.quickTo(magnetic.current, 'x', { 10 | duration: 1, 11 | ease: 'elastic.out(1, 0.3)', 12 | }); 13 | const yTo = gsap.quickTo(magnetic.current, 'y', { 14 | duration: 1, 15 | ease: 'elastic.out(1, 0.3)', 16 | }); 17 | 18 | magnetic.current.addEventListener('mousemove', (e) => { 19 | const { clientX, clientY } = e; 20 | const { height, width, left, top } = 21 | magnetic.current.getBoundingClientRect(); 22 | const x = clientX - (left + width / 2); 23 | const y = clientY - (top + height / 2); 24 | xTo(x * 0.35); 25 | yTo(y * 0.35); 26 | }); 27 | magnetic.current.addEventListener('mouseleave', (e) => { 28 | xTo(0); 29 | yTo(0); 30 | }); 31 | }, []); 32 | 33 | return React.cloneElement(children, { ref: magnetic }); 34 | }; 35 | export default Magnetic; 36 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Contact from '@/components/contact/Contact'; 4 | import Description from '@/components/description/Description'; 5 | import Landing from '@/components/landing/Landing'; 6 | import Preloader from '@/components/preloader/Preloader'; 7 | import Projects from '@/components/projects/Projects'; 8 | import SlidingImages from '@/components/slidingImages/SlidingImages'; 9 | import { AnimatePresence } from 'framer-motion'; 10 | import { useEffect, useState } from 'react'; 11 | 12 | export default function Home() { 13 | const [isLoading, setIsloading] = useState(true) 14 | useEffect(() => { 15 | (async () => { 16 | const LocomotiveScroll = (await import('locomotive-scroll')).default; 17 | const locomotiveScroll = new LocomotiveScroll(); 18 | })(); 19 | 20 | setTimeout(() => { 21 | setIsloading(false) 22 | document.body.style.cursor = 'default' 23 | window.scrollTo(0,0) 24 | },2000) 25 | }, []); 26 | 27 | return ( 28 |
29 | 30 | {isLoading && } 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/common/button/Button.jsx: -------------------------------------------------------------------------------- 1 | import gsap from "gsap"; 2 | import { ButtonHTMLAttributes, ReactNode, useEffect, useRef } from "react"; 3 | 4 | 5 | 6 | const Button = ({children, backgroundColor='#455CE9', ...attributes}) => { 7 | const circle = useRef(null); 8 | let timeline = useRef(null); 9 | let timeoutId = null; 10 | useEffect(() => { 11 | timeline.current = gsap.timeline({ paused: true }); 12 | timeline.current 13 | .to( 14 | circle.current, 15 | { top: '-25%', width: '150%', duration: 0.4, ease: 'power3.in' }, 16 | 'enter' 17 | ) 18 | .to( 19 | circle.current, 20 | { top: '-150%', width: '125%', duration: 0.25 }, 21 | 'exit' 22 | ); 23 | }, []); 24 | 25 | const manageMouseEnter = () => { 26 | if (timeoutId) clearTimeout(timeoutId); 27 | timeline.current.tweenFromTo('enter', 'exit'); 28 | }; 29 | 30 | const manageMouseLeave = () => { 31 | timeoutId = setTimeout(() => { 32 | timeline.current.play(); 33 | }, 300); 34 | }; 35 | 36 | return ( 37 |
{ 40 | manageMouseEnter(); 41 | }} 42 | onMouseLeave={() => { 43 | manageMouseLeave(); 44 | }} 45 | {...attributes} 46 | > 47 | {children} 48 |
49 |
50 | ); 51 | } 52 | export default Button -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /src/components/projects/Projects.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useState } from 'react'; 4 | import Project from './projectComponents/project/Project'; 5 | import Modal from './projectComponents/modal/Modal'; 6 | import Magnetic from '@/common/magnetic/Magnetic'; 7 | 8 | const Projects = () => { 9 | const projects = [ 10 | { 11 | title: 'C2 Montreal', 12 | src: 'c2montreal.png', 13 | color: '#000000', 14 | }, 15 | { 16 | title: 'Office Studio', 17 | src: 'officestudio.png', 18 | color: '#8C8C8C', 19 | }, 20 | { 21 | title: 'Locomotive', 22 | src: 'locomotive.png', 23 | color: '#EFE8D3', 24 | }, 25 | { 26 | title: 'Silencio', 27 | src: 'silencio.png', 28 | color: '#706D63', 29 | }, 30 | ]; 31 | 32 | const [modal, setModal] = useState({ active: false, index: 0 }); 33 | 34 | return ( 35 |
36 |
37 | {projects.map((project, index) => { 38 | return ( 39 | 45 | ); 46 | })} 47 |
48 | 49 |
50 |

More work

51 |
52 |
53 | 54 | 55 | 56 |
57 | ); 58 | } 59 | 60 | export default Projects; -------------------------------------------------------------------------------- /src/components/header/nav/Nav.tsx: -------------------------------------------------------------------------------- 1 | import NavLink from './navLink/NavLink'; 2 | import {motion} from 'framer-motion' 3 | import {menuSlide} from '../anim.js' 4 | import Curve from './curve/Curve'; 5 | 6 | interface NavProps { 7 | setIsActive: React.Dispatch>; 8 | } 9 | 10 | const navItems = [ 11 | { 12 | title: 'Home', 13 | href: '/', 14 | }, 15 | { 16 | title: 'Work', 17 | href: '/', 18 | }, 19 | { 20 | title: 'About', 21 | href: '/', 22 | }, 23 | { 24 | title: 'Contact', 25 | href: '/', 26 | }, 27 | ]; 28 | 29 | const Nav = ({setIsActive}: NavProps) => { 30 | return ( 31 | 38 |
39 |
40 |
41 |

Navigation

42 |
43 | 44 | {navItems.map((item, index) => { 45 | return ; 46 | })} 47 |
48 |
49 |
50 |

Socials

51 |
52 |
53 | Awwwards 54 | Instagram 55 | Dribble 56 | LinkedIn 57 |
58 |
59 |
60 | 61 | 62 |
63 | ); 64 | }; 65 | export default Nav; 66 | -------------------------------------------------------------------------------- /src/components/landing/Landing.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import Image from 'next/image'; 4 | import gsap from 'gsap'; 5 | import { useLayoutEffect, useRef } from 'react'; 6 | import { ScrollTrigger } from 'gsap/ScrollTrigger'; 7 | import {motion} from 'framer-motion' 8 | import {slideUp} from './animation.js' 9 | 10 | const Landing = () => { 11 | const firstText = useRef(null); 12 | const secondText = useRef(null); 13 | const slider = useRef(null); 14 | let xPercent = 0; 15 | let direction = -1; 16 | 17 | useLayoutEffect(() => { 18 | gsap.registerPlugin(ScrollTrigger); 19 | requestAnimationFrame(animation); 20 | 21 | gsap.to(slider.current, { 22 | scrollTrigger: { 23 | trigger: document.documentElement, 24 | start: 0, 25 | end: window.innerHeight, 26 | scrub: true, 27 | onUpdate: (e) => (direction = e.direction * -1), 28 | }, 29 | x: '-=300px', 30 | }); 31 | }, []); 32 | 33 | const animation = () => { 34 | if (xPercent <= -100) { 35 | xPercent = 0; 36 | } 37 | if (xPercent > 0) { 38 | xPercent = -100; 39 | } 40 | gsap.set(firstText.current, { xPercent: xPercent }); 41 | gsap.set(secondText.current, { xPercent: xPercent }); 42 | xPercent += 0.05 * direction; 43 | requestAnimationFrame(animation); 44 | }; 45 | 46 | return ( 47 | 48 | dennis 55 | 56 | {/* slider */} 57 |
58 |
59 |

63 | Dennis Snellenberg - 64 |

65 |

69 | Dennis Snellenberg - 70 |

71 |
72 |
73 |
74 | ); 75 | }; 76 | 77 | export default Landing; 78 | -------------------------------------------------------------------------------- /src/components/description/Description.tsx: -------------------------------------------------------------------------------- 1 | import {motion} from 'framer-motion' 2 | import {slideUp, opacity} from './animation' 3 | import { useInView } from 'framer-motion'; 4 | import { useRef } from 'react'; 5 | import Magnetic from '../../common/magnetic/Magnetic.jsx' 6 | 7 | const Description = () => { 8 | const container = useRef(null) 9 | const isInView = useInView(container) 10 | 11 | const phrase = 12 | 'Helping brands to stand out in the digital era. Together we will set the new status quo. No nonsense, always on the cutting edge.'; 13 | return ( 14 |
18 |
19 |

20 | {phrase.split(' ').map((word, index) => { 21 | return ( 22 | 26 | 33 | {word} 34 | 35 | 36 | ); 37 | })} 38 |

39 | 45 | The combination of my passion for design, code & interaction positions 46 | me in a unique place in the web design world. 47 | 48 |
49 | 50 |
51 |

About me

52 |
53 |
54 | 55 |
56 |
57 |
58 | ); 59 | } 60 | export default Description -------------------------------------------------------------------------------- /src/components/preloader/Preloader.tsx: -------------------------------------------------------------------------------- 1 | import {motion} from 'framer-motion' 2 | import { slideUp, opacity } from './animation.js' 3 | import { useEffect, useState } from 'react'; 4 | 5 | const words = [ 6 | 'Hello', 7 | 'Bonjour', 8 | 'Ciao', 9 | 'Olà', 10 | 'やあ', 11 | 'Hallå', 12 | 'Guten tag', 13 | 'Hallo', 14 | ]; 15 | 16 | 17 | const Preloader = () => { 18 | const [index, setIndex] = useState(0) 19 | const [dimension, setDimension] = useState({ width: 0, height: 0 }); 20 | 21 | useEffect(() => { 22 | setDimension({width: window.innerWidth, height: window.innerHeight}) 23 | },[]) 24 | 25 | useEffect(() => { 26 | if(index == words.length - 1) return; 27 | setTimeout(() => { 28 | setIndex(index + 1) 29 | }, index == 0 ? 1000 : 150) 30 | 31 | },[index]) 32 | 33 | const initialPath = `M0 0 L${dimension.width} 0 L${dimension.width} ${ 34 | dimension.height 35 | } Q${dimension.width / 2} ${dimension.height + 300} 0 ${ 36 | dimension.height 37 | } L0 0`; 38 | 39 | const targetPath = `M0 0 L${dimension.width} 0 L${dimension.width} ${ 40 | dimension.height 41 | } Q${dimension.width / 2} ${dimension.height} 0 ${ 42 | dimension.height 43 | } L0 0`; 44 | 45 | const curve = { 46 | initial: { 47 | d: initialPath, 48 | transition: { duration: 0.7, ease: [0.76, 0, 0.24, 1] }, 49 | }, 50 | exit: { 51 | d: targetPath, 52 | transition: { duration: 0.7, ease: [0.76, 0, 0.24, 1], delay: 0.3 }, 53 | }, 54 | }; 55 | 56 | return ( 57 | 63 | {dimension.width > 0 && ( 64 | <> 65 | 71 | 72 | {words[index]} 73 | 74 | 75 | 81 | 82 | 83 | )} 84 | 85 | ); 86 | } 87 | export default Preloader -------------------------------------------------------------------------------- /src/components/slidingImages/SlidingImages.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import {useRef} from 'react' 3 | import { useScroll, useTransform, motion } from 'framer-motion'; 4 | 5 | const slider1 = [ 6 | { 7 | color: '#e3e5e7', 8 | src: 'c2.jpg', 9 | }, 10 | { 11 | color: '#d6d7dc', 12 | src: 'decimal.jpg', 13 | }, 14 | { 15 | color: '#e3e3e3', 16 | src: 'funny.jpg', 17 | }, 18 | { 19 | color: '#21242b', 20 | src: 'google.jpg', 21 | }, 22 | ]; 23 | 24 | const slider2 = [ 25 | { 26 | color: '#d4e3ec', 27 | src: 'maven.jpg', 28 | }, 29 | { 30 | color: '#e5e0e1', 31 | src: 'panda.jpg', 32 | }, 33 | { 34 | color: '#d7d4cf', 35 | src: 'powell.jpg', 36 | }, 37 | { 38 | color: '#e1dad6', 39 | src: 'wix.jpg', 40 | }, 41 | ]; 42 | 43 | const SlidingImages = () => { 44 | const container = useRef(null) 45 | const {scrollYProgress} = useScroll({ 46 | target: container, 47 | offset: ['start end', 'end start'] 48 | }) 49 | 50 | const x1 = useTransform(scrollYProgress, [0, 1], [0, 150]) 51 | const x2 = useTransform(scrollYProgress, [0, 1], [0, -150]) 52 | const height = useTransform(scrollYProgress, [0, 0.9], [50, 0]) 53 | 54 | return ( 55 |
59 | 63 | {slider1.map((project, index) => { 64 | const { color, src } = project; 65 | return ( 66 |
71 |
72 | 78 |
79 |
80 | ); 81 | })} 82 |
83 | 87 | {slider2.map((project, index) => { 88 | const { color, src } = project; 89 | return ( 90 |
95 |
96 | 102 |
103 |
104 | ); 105 | })} 106 |
107 | 108 |
112 |
113 |
114 | ); 115 | } 116 | export default SlidingImages -------------------------------------------------------------------------------- /src/components/projects/projectComponents/modal/Modal.tsx: -------------------------------------------------------------------------------- 1 | 'useClient'; 2 | 3 | import Image from 'next/image'; 4 | import { motion } from 'framer-motion'; 5 | import gsap from 'gsap'; 6 | import { useEffect, useRef } from 'react'; 7 | 8 | interface Project { 9 | title: string; 10 | src: string; 11 | color: string; 12 | } 13 | 14 | interface ModalProps { 15 | modal: { 16 | active: boolean; 17 | index: number; 18 | }; 19 | projects: Project[]; 20 | } 21 | 22 | const scaleAnimation = { 23 | initial: { 24 | scale: 0, 25 | x: '-50%', 26 | y: '-50%', 27 | }, 28 | open: { 29 | scale: 1, 30 | x: '-50%', 31 | y: '-50%', 32 | transition: { duration: 0.4, ease: [0.76, 0, 0.24, 1] }, 33 | }, 34 | close: { 35 | scale: 0, 36 | x: '-50%', 37 | y: '-50%', 38 | transition: { duration: 0.4, ease: [0.32, 0, 0.67, 0] }, 39 | }, 40 | }; 41 | 42 | const Modal = ({ modal, projects }: ModalProps) => { 43 | const { active, index } = modal; 44 | const container = useRef(null); 45 | const cursor = useRef(null); 46 | const cursorLabel = useRef(null); 47 | 48 | useEffect(() => { 49 | const moveContainerX = gsap.quickTo(container.current, 'left', { 50 | duration: 0.8, 51 | ease: 'back.out', 52 | }); 53 | const moveContainerY = gsap.quickTo(container.current, 'top', { 54 | duration: 0.8, 55 | ease: 'back.out', 56 | }); 57 | 58 | const moveCursorX = gsap.quickTo(cursor.current, 'left', { 59 | duration: 0.5, 60 | ease: 'back.out', 61 | }); 62 | const moveCursorY = gsap.quickTo(cursor.current, 'top', { 63 | duration: 0.5, 64 | ease: 'back.out', 65 | }); 66 | 67 | const moveCursorLabelX = gsap.quickTo(cursorLabel.current, 'left', { 68 | duration: 0.5, 69 | ease: 'back.out', 70 | }); 71 | const moveCursorLabelY = gsap.quickTo(cursorLabel.current, 'top', { 72 | duration: 0.5, 73 | ease: 'back.out', 74 | }); 75 | 76 | window.addEventListener('mousemove', (e) => { 77 | const { clientX, clientY } = e; 78 | moveContainerX(clientX); 79 | moveContainerY(clientY); 80 | 81 | moveCursorX(clientX); 82 | moveCursorY(clientY); 83 | 84 | moveCursorLabelX(clientX); 85 | moveCursorLabelY(clientY); 86 | }); 87 | }, []); 88 | 89 | return ( 90 | <> 91 | 98 |
102 | {projects.map((project, index) => { 103 | const { title, src, color } = project; 104 | return ( 105 |
110 | {title} 117 |
118 | ); 119 | })} 120 |
121 |
122 | 129 | 136 | View 137 | 138 | 139 | ); 140 | }; 141 | export default Modal; 142 | -------------------------------------------------------------------------------- /src/components/header/Header.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React, { useState, useRef, useLayoutEffect } from 'react'; 4 | import Nav from './nav/Nav'; 5 | import { AnimatePresence, motion } from 'framer-motion'; 6 | import Link from 'next/link'; 7 | import gsap from 'gsap'; 8 | import { ScrollTrigger } from 'gsap/ScrollTrigger'; 9 | import Magnetic from '../../common/magnetic/Magnetic.jsx'; 10 | 11 | const Header = () => { 12 | const [isActive, setIsActive] = useState(false); 13 | const burger = useRef(null); 14 | 15 | const handleClick = () => { 16 | if (!isActive) { 17 | setIsActive(true); 18 | } else { 19 | setIsActive(false); 20 | } 21 | }; 22 | 23 | useLayoutEffect(() => { 24 | gsap.registerPlugin(ScrollTrigger); 25 | 26 | gsap.to(burger.current, { 27 | scrollTrigger: { 28 | trigger: document.documentElement, 29 | start: 0, 30 | end: window.innerHeight, 31 | onLeave: () => { 32 | gsap.to(burger.current, { 33 | scale: 1, 34 | duration: 0.25, 35 | ease: 'power1.out', 36 | }); 37 | }, 38 | onEnterBack: () => { 39 | gsap.to(burger.current, { 40 | scale: 0, 41 | duration: 0.25, 42 | ease: 'power1.out', 43 | }); 44 | }, 45 | }, 46 | }); 47 | }, []); 48 | 49 | return ( 50 |
51 |
52 |

53 | © 54 |

55 | 56 |
57 |

58 | Code by 59 |

60 |

61 | Dennis 62 |

63 |

64 | Snellenberg 65 |

66 |
67 |
68 | 69 |
70 | 71 |
72 | Work 73 |
74 |
75 |
76 | 77 | 78 |
79 | About 80 |
81 |
82 |
83 | 84 | 85 |
86 | Contact 87 |
88 |
89 |
90 |
91 | 92 | {/* burger button */} 93 |
100 |
105 |
110 |
111 | 112 | 113 | {isActive && ( 114 | <> 115 | setIsActive(false)} 121 | /> 122 |
127 | ); 128 | }; 129 | export default Header; 130 | -------------------------------------------------------------------------------- /src/components/contact/Contact.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | import { useRef } from 'react'; 3 | import { useScroll, useTransform, motion } from 'framer-motion'; 4 | import Magnetic from '@/common/magnetic/Magnetic'; 5 | 6 | const Contact = () => { 7 | const container = useRef(null); 8 | const { scrollYProgress } = useScroll({ 9 | target: container, 10 | offset: ['start end', 'end end'], 11 | }); 12 | 13 | const x = useTransform(scrollYProgress, [0, 1], [0, 100]); 14 | const rotate = useTransform(scrollYProgress, [0, 1], [120, 90]); 15 | const y = useTransform(scrollYProgress, [0, 1], [-200, 0]); 16 | 17 | return ( 18 | 23 |
24 |
25 | 26 |
27 | dennis 33 |
34 |

35 | Let's work 36 |

37 |
38 |

together

39 | 43 | 44 |
45 |

46 | Get in touch 47 |

48 |
49 |
50 |
51 | 60 | 64 | 65 |
66 |
67 | 68 |
69 |

info@dennissnellenberg.com

70 |
71 |
72 | 73 |
74 |

+31 6 27 84 74 30

75 |
76 |
77 |
78 |
79 |
80 | 81 |

82 | Version 83 |

84 |

85 | 2022 © Edition 86 |

87 |
88 | 89 |

90 | Local Time 91 |

92 |

93 | 11:49 PM GMT+2 94 |

95 |
96 |
97 |
98 | 99 |

100 | Socials 101 |

102 |
103 |
104 | 105 |

Awwwards

106 |
107 | 108 |

Instagram

109 |
110 | 111 |

Dribble

112 |
113 | 114 |

Linkedin

115 |
116 |
117 |
118 |
119 |
120 |
121 | ); 122 | }; 123 | export default Contact; 124 | --------------------------------------------------------------------------------