├── .eslintrc.json
├── public
├── favicon.ico
└── parallax
│ ├── mountain-3.svg
│ ├── cloud-bottom.svg
│ ├── clouds-bottom.svg
│ ├── clouds-left.svg
│ ├── clouds-right.svg
│ ├── sun.svg
│ ├── mountain-2.svg
│ ├── mountain-1.svg
│ └── stars.svg
├── jsconfig.json
├── next.config.js
├── src
├── pages
│ ├── _app.js
│ ├── _document.js
│ └── index.js
├── styles
│ └── globals.css
└── components
│ └── Parallax.js
├── README.md
├── package.json
└── .gitignore
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IvanSmiths/tutorial-next-gsap-parallax/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | }
5 |
6 | module.exports = nextConfig
7 |
--------------------------------------------------------------------------------
/src/pages/_app.js:
--------------------------------------------------------------------------------
1 | import '@/styles/globals.css'
2 |
3 | export default function App({ Component, pageProps }) {
4 | return
5 | }
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Next.js Parallax Effect With Gsap ScrollTrigger
2 |
3 | Repository of the [YouTube video](https://youtu.be/alGnk3iMaYE) on how to make a parallax effect using Next.js and Gsap ScrollTrigger.
4 |
5 | ## Stack used
6 |
7 | - Next.js
8 | - Gsap ScrollTrigger
9 | - CSS
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tutorial-next-gsap-parallax",
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 | "eslint": "8.36.0",
13 | "eslint-config-next": "13.2.4",
14 | "gsap": "^3.11.5",
15 | "next": "13.2.4",
16 | "react": "18.2.0",
17 | "react-dom": "18.2.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.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 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
--------------------------------------------------------------------------------
/public/parallax/mountain-3.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/pages/_document.js:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from 'next/document'
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/public/parallax/cloud-bottom.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/public/parallax/clouds-bottom.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import Parallax from '@/components/Parallax'
2 |
3 | function Home() {
4 | return (
5 | <>
6 |
7 |
8 |
9 | Lorem ipsum
10 |
11 |
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit laborum ipsam corrupti asperiores magnam quos cumque animi tempore vero repellendus, harum odio neque quis, non temporibus. Inventore asperiores repudiandae praesentium ut, fugit quo esse, placeat ullam quibusdam perspiciatis delectus ducimus nihil. Dolorum nam veniam aperiam sapiente corporis! Quisquam, veritatis repellendus?
12 |
13 | >
14 | )
15 | }
16 |
17 | export default Home
--------------------------------------------------------------------------------
/public/parallax/clouds-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/public/parallax/clouds-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/public/parallax/sun.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/public/parallax/mountain-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/styles/globals.css:
--------------------------------------------------------------------------------
1 | *,
2 | html,
3 | body {
4 | margin: 0;
5 | padding: 0;
6 | box-sizing: border-box;
7 | }
8 |
9 | :root {
10 | --primaryColor: #282a57;
11 | --secondaryColor: #e4e4e4;
12 | }
13 |
14 | body {
15 | font-family: 'Montserrat', sans-serif;
16 | background-color: var(--primaryColor);
17 | color: var(--secondaryColor);
18 | }
19 |
20 | .parallax-outer {
21 | overflow: hidden;
22 | }
23 |
24 | .parallax {
25 | height: 110vh;
26 | width: 100%;
27 | position: relative;
28 | }
29 |
30 | .parallax img {
31 | position: absolute;
32 | }
33 |
34 |
35 | .mountain-3,
36 | .mountain-2,
37 | .mountain-1 {
38 | width: 100%;
39 | bottom: 0;
40 | z-index: 3;
41 | }
42 |
43 | .mountain-2 {
44 | bottom: 20px;
45 | z-index: 2;
46 | }
47 |
48 | .mountain-1 {
49 | bottom: 40px;
50 | z-index: 1;
51 | }
52 |
53 | .sun {
54 | top: 70%;
55 | left: 50%;
56 | transform: translate(-50%, -50%);
57 | width: 40%;
58 | }
59 |
60 | .clouds-left {
61 | left: 0;
62 | width: 20%;
63 | }
64 |
65 | .clouds-right {
66 | right: 0;
67 | width: 20%;
68 | }
69 |
70 | .clouds-bottom {
71 | bottom: 0px;
72 | width: 100%;
73 | }
74 |
75 | .stars {
76 | top: -550px;
77 | left: 0;
78 | width: 100%;
79 | }
80 |
81 | .copy {
82 | position: absolute;
83 | bottom: 0%;
84 | left: 50%;
85 | transform: translate(-50%, -50%);
86 | z-index: 0;
87 | color: var(--secondaryColor);
88 | display: flex;
89 | justify-content: center;
90 | align-items: center;
91 | flex-direction: column;
92 | opacity: 0;
93 | }
94 |
95 | .copy h1 {
96 | font-size: 10rem;
97 | }
98 |
99 | .copy span {
100 | background-color: var(--secondaryColor);
101 | color: var(--primaryColor);
102 | padding: 1rem;
103 | font-weight: 800;
104 | border-radius: 0.5rem;
105 | opacity: 0;
106 | }
107 |
108 | .about {
109 | color: var(--secondaryColor);
110 | padding-left: 10%;
111 | padding-bottom: 20%;
112 | }
113 |
114 | .about h2 {
115 | color: var(--secondaryColor);
116 | font-size: 5rem;
117 | }
118 |
119 | .about p {
120 | margin-top: 20px;
121 | width: 50%;
122 | }
--------------------------------------------------------------------------------
/public/parallax/mountain-1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/components/Parallax.js:
--------------------------------------------------------------------------------
1 | import { useRef, useEffect, useState } from "react";
2 | import { gsap } from "gsap";
3 | import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
4 |
5 | function Parallax() {
6 |
7 | const [background, setBackground] = useState(20)
8 |
9 | const parallaxRef = useRef(null);
10 | const mountain3 = useRef(null);
11 | const mountain2 = useRef(null);
12 | const mountain1 = useRef(null);
13 | const cloudsBottom = useRef(null);
14 | const cloudsLeft = useRef(null);
15 | const cloudsRight = useRef(null);
16 | const stars = useRef(null);
17 | const sun = useRef(null);
18 | const copy = useRef(null);
19 | const btn = useRef(null);
20 |
21 | useEffect(() => {
22 | let ctx = gsap.context(() => {
23 | gsap.registerPlugin(ScrollTrigger);
24 | var tl = gsap.timeline({
25 | defaults: { duration: 1 },
26 | scrollTrigger: {
27 | trigger: parallaxRef.current,
28 | start: "top top",
29 | end: "5000 bottom",
30 | scrub: true,
31 | pin: true,
32 | onUpdate: (self) => {
33 | setBackground(Math.ceil(self.progress * 100 + 20))
34 | },
35 | },
36 | });
37 | tl.to(
38 | mountain3.current,
39 | {
40 | y: "-=80",
41 | },
42 | 0
43 | );
44 | tl.to(
45 | mountain2.current,
46 | {
47 | y: "-=30",
48 | },
49 | 0
50 | );
51 | tl.to(
52 | mountain1.current,
53 | {
54 | y: "+=50",
55 | },
56 | 0
57 | );
58 | tl.to(
59 | stars.current,
60 | {
61 | top: 0,
62 | },
63 | 0.5
64 | );
65 | tl.to(
66 | cloudsBottom.current,
67 | {
68 | opacity: 0,
69 | duration: 0.5
70 | },
71 | 0
72 | );
73 | tl.to(
74 | cloudsLeft.current,
75 | {
76 | x: "-20%",
77 | opacity: 0,
78 | },
79 | 0
80 | );
81 | tl.to(
82 | cloudsRight.current,
83 | {
84 | x: "20%",
85 | opacity: 0,
86 | },
87 | 0
88 | );
89 | tl.to(
90 | sun.current,
91 | {
92 | y: "+=210",
93 | },
94 | 0
95 | );
96 | tl.to(
97 | copy.current,
98 | {
99 | y: "-250%",
100 | opacity: 1
101 | },
102 | 0
103 | );
104 | tl.to(
105 | btn.current,
106 | {
107 | opacity: 1,
108 | },
109 | 1.5
110 | );
111 | });
112 | return () => ctx.revert();
113 | }, []);
114 |
115 | return (
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
Journey
128 | Discover more
129 |
130 |
131 |
132 | )
133 | }
134 |
135 | export default Parallax
--------------------------------------------------------------------------------
/public/parallax/stars.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------