Learn to build a premium MacBook Pro landing page clone using React, Three.js, GSAP, and Tailwind CSS with advanced scroll animations and 3D models.
Key Takeaways
- You can recreate premium, interactive web experiences similar to Apple's using modern frontend tools.
- Combining React, Three.js, GSAP, and Tailwind CSS enables advanced animations and 3D interactivity.
- Proper hosting and domain setup enhances professionalism and credibility for your projects.
- Code Rabbit's tools help maintain high code quality with smart PR reviews and CLI integration.
- This project serves as a valuable career asset to showcase your frontend development skills.
What the video covers
- Explore how Apple creates immersive web experiences with smooth 3D animations and premium scroll effects.
- Build a cinematic hero section with scroll-synchronized video backgrounds and interactive photorealistic 3D MacBook models.
- Implement parallax masked video rendering and scroll trigger effects for precise animation control.
- Use ReactJS for dynamic UI, Tailwind CSS for styling, Three.js for 3D model animation, and GSAP for smooth transitions and timelines.
- Follow best coding practices with Code Rabbit assisted code reviews and CLI tools.
- Access a free video kit including GSAP and Three.js guides, Tailwind CSS crash course, source code, Figma designs, and assets.
- Learn how to host your project professionally with Hostinger, including domain registration and SSL setup.
- Adapt the project to any frontend framework like Next.js, Angular, or Vue.
- Use JetBrains IDEs for enhanced coding experience with advanced autocompletion and git integration.
- Gain career-ready skills by creating a polished project that can serve as a professional portfolio piece.
Chapters
- 00:00Introduction to Apple's Premium Web Experience
- 07:21Choosing Frameworks and Setting Up Code Editor
- 14:57Creating Navigation Bar Component
- 24:18Using Code Rabbit for Code Reviews and CLI Setup
- 31:15Rendering MacBook Pro Model and UI Components
- 47:17Installing Packages and Responsive Animation Logic
- 63:17Animating Video Backgrounds and Scroll Triggers
- 85:34Finalizing Animations and Responsive Layouts
- 115:51Loading States and Displaying 3D Models
- 123:42Scroll Triggered Animations for Content Sections
Full Transcript — Download SRT & Markdown
Speaker A
Have you ever wondered how Apple makes you feel like you absolutely need that sleek MacBook Pro just by scrolling through their website? Those buttery smooth animations. The way the laptop rotates in perfect 3D space. How every scroll feels premium. And suddenly,
Speaker A
you're calculating if you can survive on ramen for 3 months just to be able to afford it. See, Apple doesn't just build websites. They build experiences that make an impact that eventually makes you buy that new Apple product that you
Speaker A
don't really need. But here's the question. What if you could do it, too? What if every website you create could also make your users stop mid-scroll and think, "Holy crap, how did they just do that?" What if your work didn't just wow
Speaker A
some random visitors, but rather turn them into customers or even landed you that next big gig or that job? Hi there and welcome to a brand new video where I'll teach you how to create a MacBook Pro landing page clone that looks and
Speaker A
feels exactly like Apple's masterpiece. You'll learn to develop cinematic hero sections with scroll synchronized video backgrounds.
Speaker A
Interactive photorealistic 3D MacBook models that users can play with. Parallax masked video rendering that feels alive.
Speaker A
Scroll trigger effects giving us precise control over animations and timelines. Advanced scroll storytelling that guides users through an emotional journey. A sleek bento box layout that presents content in visually balanced modular sections. All of it with optimized performance and responsive design that
Speaker A
works flawlessly across all devices. And we're not just going to copy Apple features here. You'll actually learn the skills behind them, including ReactJS for dynamic scalable UI rendering, Tailwind CSS for pixel perfect styling, 3JS to load, light up, and animate
Speaker A
realistic 3D models. GSAP for scroll triggers, timelines, sequencing, and buttery smooth transitions. And because we're not vibe coders but developers, we'll implement all the best code practices and guidelines using Code Rabbit assisted code reviews and their new CLI to make your project feel as
Speaker A
premium as Apple's own site under the hood. To push you even further, I've prepared a complete GSAP and 3JS guide packed with pro tips, examples, and advanced project ideas. You can find it within the video kit down below,
Speaker A
completely for free. And if you really want to go beyond just following along, if you want to understand the psychology behind why these animations grab attention, hold users in place, and make a product feel irresistible, and then learn how to design and build your own
Speaker A
from scratch, I've put all of that into my GSAP Mastery course. That way, you won't be just copying Apple's animations. You'll be creating experiences that feel just as premium entirely on your own. The link is down in the description. So, I hope you're ready
Speaker A
to stop just admiring Apple's designs and start creating them yourself. Let's get going. And since my goal is to make sure you land a job, this app is not just a regular project. It's a career asset designed to showcase everything you're
Speaker A
capable of, making you an attractive candidate for any job. See, hosting your website on a personalized domain makes you more professional and improves your credibility. It also highlights your skill in self-hosting, scaling, and managing websites. So, before we dive
Speaker A
into the code, we need to set the foundation. I highly recommend Hostinger for hosting this app, and they're offering an amazing deal right now. So click the Hostinger link in the description, and then click start. Now when it comes to hosting this app, I'll
Speaker A
personally go with a premium plan. It allows you to host 25 websites with the same account. Has 25 gigs of SSD storage. You get a free domain for one year and free SSL for HTTPS security, which builds trust with anybody visiting
Speaker A
your site. This deal won't last forever. And because I partnered with Hostinger, you'll get an even bigger discount. So click the link in the description, start now, and choose a plan. Here, we have to choose the period of our hosting. And
Speaker A
with the amazing deal going on right now, I'll choose 48 months to save the most. If you already know the domain name you want to get with the hosting, you can type it out right here or you can do that later. And most importantly,
Speaker A
here's that additional coupon code that I promised. Just type in JavaScript Mastery and you'll get an even bigger discount on your hosting package. You can now continue and very soon we'll start developing our app. Once you're in, we can continue with Hostinger's
Speaker A
guided setup. In this case, we're creating a new app. Since we are real developers, we won't be using WordPress or the website builder, but rather we'll start with an empty website. And now here you can choose your domain name. I
Speaker A
know we're still early in the project. We haven't yet started building it, but you've seen the intro and you might already have an idea about how you want to name it. So just type in your domain name right here. As you can see, I'm
Speaker A
already using Hostinger for all of my domains. Or if you're really not sure, you can use a temporary domain for now and then choose a real one later on. And that's it. Your domain will get registered. Next, you'll have to choose
Speaker A
a server closest to where you think your users will be so your website loads fastest. Feel free to choose anything here. And that's it. The setup is now initializing. While that's happening, I want to let you know that there's a
Speaker A
complete GitHub repo for this entire code which is entirely free. You can click the video kit link down in the description and there you'll be able to learn more about JSMy Pro, our platform for premium courses. But here you'll
Speaker A
also be able to find a completely free Tailwind guide, 3JS guide, Tailwind CSS crash course, and the source code for the code alongside the entire Figma design and the assets. So everything you need regarding this project will be
Speaker A
right here completely for free. And that's it. Our hosting has been set up and now we are ready to dive right into the code.
Speaker A
Before we start developing this design masterpiece, let's actually set up the environment so that later on we can just focus on coding. First things first, click the Code Rabbit link down in the description and create your free account. We'll use Code Rabbit's PR
Speaker A
reviews and their new CLI all throughout this video. So, let's make sure to set it up right away. With that in mind, let's go ahead and check out the entire demo of the application you'll build today. Starting with a very simple
Speaker A
navigation bar and an animated hero section. Moving down over to a taking a closer look at the product that our users will be buying. You can also change the colors and the sizes.
Speaker A
Scrolling down, you can see this beautiful parallax video section that as you scroll actually shows you the name of the chip that's inside of that MacBook. Then a bit more info about it.
Speaker A
Finally, you have this section where these different screens reveal what you can do on the MacBook itself with more info. Finally, this beautiful animated section that tells you what you can do while at the same time showing you a
Speaker A
video of that happening. So, here we have image AI, email AI, summarize, AirDrop, and more. And all of that also happens when you're going back.
Speaker A
Beautiful. And finally, a nice bento grid of all the features. That's it. Super precise, shows you everything you need to know. Nothing more, nothing less. To build this app, I'll be using React, but you're completely free to choose from any of the front-end
Speaker A
frameworks. If you want to use Next.js or Angular or Vue or whatever it is, you're completely free to follow along, make small adjustments, and adapt my code to your needs. Also, make sure you've set up your code editor. I personally use
Speaker A
JetBrains's IDEs because of their advanced auto-completion, error tracking, advanced integrated git workflows, and overall better code intelligence. It's a full IDE after all and with it, you can also get Jun
Speaker A
useful, and I'll show you how we can use it within this application to speed up your development workflow. That said let's go ahead and create our application. We'll use Vit to quickly spin up our React app. So head over to
Speaker A
Vit and click get started. Then if you scroll down a bit, you'll see a command for running Vit. So just copy it. Or if you're using WebStorm like I am, you can just go ahead and create a new project.
Speaker A
Immediately choose Vit right here and then choose a V template. In this case I'll go with React. This will run the same command for me. And you can name the folder something like MacBook Gap app. Once you're within it, you can run
Speaker A
the command you copied over from Vit and make sure to add the dot slash at the end so it creates it in the current repository. I'll say remove existing files and continue. Choose a framework.
Speaker A
In this case, we'll go with plain JavaScript, but once again, if you want to use TypeScript, you're more than welcome to and no need to use experimental versions. We're going to install with mpm and start right away.
Speaker A
There we go. The app is running on localhost 5173. So you can open it up within your browser and let's clean up the project a bit to prepare it for your app. Go ahead and remove the app.jsx within the source.
Speaker A
Remove the app CSS. Remove all of the assets since you won't be needing them. Remove all the styles from index.css.
Speaker A
And let's actually show something on the screen by creating a new app.jsx JSX file within which you can run rafce.
Speaker A
This is short for react error function with exports. If this didn't work for you, you must head over to plugins and install modern react snippets and then it'll work. If you create this app, head back over within main.jsx.
Speaker A
You'll see that it's rendering it. And if you reload your app on the browser you should be able to see a small piece of text that says app.
Speaker A
Instead of that, we can maybe render an H1 and then make it say, "Hello Apple MacBook Pro." And there we go. The text is there, which means that everything is working. Now, we got to install Tailwind CSS that we'll use for
Speaker A
styling. So head over to tailwindcss.com that allows you to rapidly build modern websites without ever leaving your HTML or in this case JSX and it'll give us those utility classes. So click get started and install it using VIT. That's the
Speaker A
selected option. We've already done the first step. So now we just have to install Tailwind CSS by copying and pasting this command. Webtorm allows me to have two terminals or more. I'll name the first one app as it's already
Speaker A
running our app and I'll create a second one which I'll call terminal within which I can run additional commands all while our app is running. I'll run mpm install tailwind css tailwind css/vit.
Speaker A
Once that is done we also have to configure the vit plugin by heading over within our vit configuration. So, copy this, head over into VIT config, and don't necessarily override this, but rather just add these two additional lines. Import tailwind CSS from Vit, and
Speaker A
then add this plug-in right here to the plugins array. Perfect. Once that is done, head over within our index.css and copy this import deal CSS line.
Speaker A
restart your application if needed by pressing Ctrl + C and then mpm rundev once again and then let's start using it. Let's copy this H1 right here that says hello world and update our current H1 that now has this not class but rather
Speaker A
class name of text 3 XL font bold and underline. If you now head back you'll see that that is applied. We can do one more maybe testing the text indigo of 700 which is a color and you can see
Speaker A
that it works. That means that you've successfully set up Tailwind. Now, as you can imagine, our final app will have a lot of different icons starting from this Apple icon at the top to this magnifier glass or the cart icons right
Speaker A
here. We got to get all of these icons and colors and themes within our application so we can later on use them.
Speaker A
I prepared all of that for you within a free Figma design. You can see the entire app will build with all of the sections one by one so that if you maybe want to give it a shot on your own, you
Speaker A
can do that as well. This design alongside the theme that it is using which are some colors that are being repeated even though it seems it's all dark. There are a couple of colors that we need to add to our theme. So the
Speaker A
design and the colors are within the video kit link down in the description. Just click it and sign up for free. Once you're there, you'll be able to learn more about JSMy Pro. It's our pro platform for those who want to learn
Speaker A
more. And below that, you'll be able to find all the necessary files that I'll be referencing throughout the rest of this course. One of those files will be a modified index.css.
Speaker A
So, head over into the index.css find it within the video kit down below and then copy and paste it right here.
Speaker A
It'll feel like there's a lot of code right here, but most of these are just some utility classes that we'll use to make the styling of our app easier. And then, as I promised, here we're setting up some fonts and setting up some of our
Speaker A
colors. Yes, most of them are just dark but here they are. In the video kit you'll also be able to find the assets.
Speaker A
These are those small icons and images that I've been telling you about. So simply click right here and then download the zip. Once you download it simply unzip it and then remove the current public folder and then drag and
Speaker A
drop the new one within the root of your application. You'll see that this one will include a couple of fonts, the 3D models we'll be using, videos, images, and icons. This way, we have everything we need to get
Speaker A
started developing our application. Congrats on going through the setup. This one will for sure be the most boring lesson in this entire course. But immediately from the next lesson onward we'll start developing our MacBook application.
Speaker A
Let's start with the first piece of our UI, the navbar. Create a new folder within the source folder and call it components. React applications are all about components, very small pieces of UI. So in this one, we'll create a new
Speaker A
file and I'll call it navbar.jsx within which you can run rafce just to be able to see the text that says navbar and then import it right within our app.
Speaker A
Instead of the h1, we'll wrap everything into the main tag because this is the main part of our application. And there I will render the navbar. And now you can just see a piece of text that says navbar. So now let's head over into this
Speaker A
component and let's start implementing it. First things first, I'll turn this div into an HTML 5 semantic header tag.
Speaker A
And within it, I'll create a new nav tag. Within nav, I'll render a new image that'll have a source equal to/lo.svg with an al tag of apple logo. And immediately we can see it at the top.
Speaker A
Then below it, still within the nav we'll render a new ul an unordered list.
Speaker A
And within here, we have to create some nav link items. So you can create an object and then within it a new array.
Speaker A
Within that array, we can create different links. For example, we can create an object with a label that'll say something like store. Then you can create another object that'll be a label of let's do Mac.
Speaker A
Maybe another one that'll be all about not Linux. We're not doing that right here. Maybe iPhone, right?
Speaker A
We can do another one for a label of watch or Apple Watch. Let's do another one for vision.
Speaker A
And then finally the last one for the AirPods. Okay. So now we have an array of a couple of objects with a label property.
Speaker A
I'll give it some breathing room. And then I'll now map over this array by saying map where we get each individual link. And for each one, we want to automatically return something. When I say automatic return, I mean that you
Speaker A
immediately put the parentheses right here and not curly braces. Because if you put curly braces, then you have to explicitly say what you want to return.
Speaker A
But if you immediately put parenthesis right here, that means that you don't have to add the return keyword explicitly because it is implied. So for each one, I want to return a link, an LI with a key equal to link.l.
Speaker A
And within it, I'll put an anchor tag with an href of link.l. For now, these are not valid labels, but later on we can update them if you want to. And then the text will say link.l.
Speaker A
If you want to, since we are repeating the word label too much, you can dstructure the label from the link. And that way, you don't have to repeat the word link three times. That'll look something like this. If you save it now
Speaker A
you won't be able to see anything just yet. But if you expand the screen just a tiny bit, you'll be able to see that on desktop and tablet devices, the navbar is right here. And below this UL, I will
Speaker A
add a div with a class name equal to flex- center and a gap of three within which I will render a button within which there's going to be an image with a source equal to for/arch.svg SVG with an alt of search. And I will
Speaker A
duplicate this button and image one more time below. This time it'll say cart.svg with an alt of cart. So now you can see in desktop it all aligns perfectly in Apple style. Apple logo on the left, the links in the middle, and then a few
Speaker A
icons on the right. Now, if I collapse this, you can see that on mobile, the additional links disappear and we only have the Apple and two additional icons on the right. Now, to tidy up this code a bit further, we don't have to keep
Speaker A
this data right here. I will consider this some data that is maybe logic or info, definitely not the presentation of our JSX components. So, typically it's better not to keep it right here. For these types of data pieces, you can
Speaker A
create a new file. So, right within the source folder, I'll create another folder called constants.
Speaker A
And I'll create a new file called index.js within the constants folder. When you name a file index, that means that you can access it just by saying constants without having to necessarily say index.js or some other name.js.
Speaker A
Constants index is just constants. And then here you can move this array which for example we will call nav links. So now if you head over to constants you create a new const nav links and you paste this array. It's better to keep it
Speaker A
here. You can immediately export it and then you can go ahead and import it right here. Nav links coming from constants index.js GS or as I said you can just say constants and now we have a very clean navigation
Speaker A
bar. Perfect. That was pretty simple right? So now that we've implemented our first UI component, no matter how simple it is, I want to teach you how to immediately start utilizing all the best practices within your development process. One thing is to not commit to
Speaker A
Git after your entire project is done. You want to create a new GitHub repo right from the start. So head over to github.com/new and right here you can choose an owner and enter a repository name. In this case I'll call it GAP MacBook landing.
Speaker A
You can give it a description if you want to. And you can leave it public and click create repo. Once you do it right here you'll get the instructions needed to connect this local repo to the remote one. So let's open up our terminal. Say
Speaker A
get init add dot get commit-m initial commit get branch- m main get remote add origin get push u origin main. With just these couple of commands you will have pushed the current code and the assets over to your GitHub repo. So if you reload in a
Speaker A
couple, of, seconds,, you'll, be able, to, see all your code pushed right here to GitHub. And that is important for a couple of reasons. First of all, other developers and potential employers will be able to track your commit history to
Speaker A
see whether you have actually built this project or you just copy and pasted it from a tutorial. If you've been building it yourself, then all the commits will show up nicely. You can also tidy up the description, enter the URL, and some
Speaker A
topics. This will make the repo cleaner and at the same time as you're pushing new code to your repos, your contributions graph will shine green. So from here on, we'll push over to GitHub for almost every section in this course and then you'll
Speaker A
be able to go to my repo and if you're not sure how something was implemented you can check that specific commit and see exactly what happened. That'll make it easier for you to follow this tutorial as well. Now, on top of setting
Speaker A
up GitHub for every single new project that I do, another tool that became a must-have for me is Code Rabbit. It allows me to keep my code clean and catch some mistakes that I miss. So, if you haven't already, click the link in
Speaker A
the description and create a free account. In this course specifically, I want to focus on their newest product called CLI reviews. Instead of Vibe coding, it allows you to vibe check your code with smart reviews. Go ahead and
Speaker A
copy this CLI command. And an important note is that Code Rabbit CLI is for now supported in Mac OS and Linux. Windows is coming soon. So if you're in Windows depending on when you're watching this you might be able to check out Code
Speaker A
Rabbit CLI, but if not, you can still create an account and utilize the pull request reviews feature. And depending on which IDE you're using, you can also check out the AI code reviews right within your IDE. But now for my Mac OS
Speaker A
and Linux users, let's quickly go through the code rabbit CLI setup. Back within the editor, I'll paste this curl command which will download the code rabbit CLI. It says installation complete. And you can now restart your shell or run this command right here
Speaker A
which I will do. Once you do that, you can run code rabbit-help to see a bit more info about what it can do or you can just code rabbit. This will automatically authenticate you. So I'll go ahead and authenticate using GitHub
Speaker A
and you'll get this token. So simply copy the token and paste it within your terminal. There we go. Code rabbit is running. Right now there are no new changes. That's because our navbar was super simple and I went ahead and
Speaker A
committed it before running code rabbit. So let's go ahead and implement the hero section and then we'll be able to see what this code rabbit has to say about our code.
Speaker A
Now that the navbar is done, let's focus on our wonderful hero section that has this animated MacBook opening to us and kind of hinting that there's a lot inside. We can get started by creating a new component and call it hero.jsx.
Speaker A
Run rafce right within it to quickly spin up that component and then use it right here below the navbar within our app.jsx. JSX. This one will be called hero. So now if you head back over to our app, you won't be able to see the
Speaker A
hero yet because it's being hidden by the navbar. So instead of a div, let's turn it into a section with an ID of hero so that we can scroll to it if needed. Within it, render a div. And
Speaker A
then within that div, render a new H1 that says MacBook Pro. That's your big title. Then below that H1, render an image with a source of forward slashtitle.png and give it an alt of MacBook title.
Speaker A
There we go. Now, right below this div render a video. Set its source to forward slash videos/hero.mpp4 and then set it to autoplay, which will automatically open it up on reload. Make it muted, make it play in line so you
Speaker A
don't see the additional settings like the volume, forward, backward buttons and so on. And now if you reload, it'll just nicely autoplay and open up. Even though it might seem like this is some crazy animation, what it actually is is
Speaker A
just a very optimized video animation that just opens up and works. We can also make it load a bit faster or slower and modify some other video properties by attaching a ref to it. This is something we'll often do. Whenever you
Speaker A
want to have more control over a specific video, you attach it to a ref.
Speaker A
So video ref is equal to a use ref hook coming from React and then you simply attach it to a video by saying ref is equal to video ref. On its own, this won't change anything, but it allows you
Speaker A
to do more stuff with it. For example you can open up a new use effect, and when this component loads, you can then check if video ref has been attached to any element. If that is the case, set
Speaker A
the video ref.curren.playback rate to two. And now, if you save it you'll see that it'll open up two times as fast. Check it out. If I comment this out now, it's much slower. It is completely up to you what you want to do
Speaker A
with this video and the other videos as well. Sometimes it is personal preference, but the goal of the websites is not to overwhelm them with animations. There should be some micro animations, but they should be happening pretty fast, not to make your website
Speaker A
feel like it's slow. And then below this video, we can render a button. This button will simply say the word buy as a primary CTA and then below it a P tag where we can say from dollar1599 or dollar133 per month for 12 months.
Speaker A
Something like this. There we go. Nice and Appleike. And of course, if you expand it on full screen, it'll look so much better because this is a horizontal animation, which means that it takes more horizontal space. Beautiful. But
Speaker A
now, let's open up our terminal. I'll head over to Code Rabbit, stop it from running, and then run the Code Rabbit command one more time. Now, it notices that there are two files changed with 27 insertions. You can now hit enter to hop
Speaker A
into the review directly within our terminal. So, press enter. And now it says AI pretending that it knows better than you. And it's loading the existential dread about your code. Okay that's nice. At least it's being honest.
Speaker A
Preparing passive aggressive comments. I love that one. I love all of these loading messages, actually. And there we go. Review completed this time with no comments. Well, that's absolutely amazing for us. Sure. Sure. The hero component wasn't that long, so there
Speaker A
wasn't much to review. But as we dive developing other absolutely more complicated components like this 3D model viewer, I'm sure Code Rabbit will find ways to improve our code. So for now, since we're good, let's just go ahead and commit it by typing get add
Speaker A
dot get commit-m implement hero section and get push. In this lesson, I'll teach you how to build this interactive 3D MacBook Pro viewer where your users can change the color of the MacBook from silver to space black. Switch between different
Speaker A
sizes as well as rotate this 3D model. This layout will be divided into three different pieces. A headline and some product info. Then the control panel for customizations, this thing right here at the bottom. and then a canvas that will
Speaker A
render that 3D model. Let's start by creating a new file in the components folder and you can call it product viewer.jsx.
Speaker A
You can use rafce to quickly add a new component within it and then you can import it right here within the app.jsx by simply saying product viewer. Now, if we head back to our current version of the application, you should be able to
Speaker A
see a product viewer text right here. Then, let's develop the UI of this experience section, head over into the product viewer, turn it into a section that'll have an ID equal to product- viewer, and then within it, render an H2
Speaker A
that'll say take a closer look. Then below it, render another div with a class name equal to controls. And within it, render a P tag with a class name equal to info that'll say MacBook Pro 16 in silver or maybe space black. There we
Speaker A
go. You can see it right here. Below this P tag, go ahead and create another div that we'll use to center the controls with a class name of flex center, a gap of five, and margin top to divide it a bit from the text.
Speaker A
Here we can render a div with a class name of color control. And there we can render two self-closing divs with a class name of BG neutral 300 and another one with BG neutral 900. And here we have two different colors. We'll always
Speaker A
be rendering one color at a time. So for now, we can maybe make it just say MacBook Pro 16 in space black. And then below this div, we'll create another div that'll have a class name equal to size
Speaker A
control. And here we can render a div with a P tag in between that'll say 14 in. And we can duplicate it and below say 16 in. So now we can choose between different colors. If you're ever wondering what a specific Tailwind class
Speaker A
name does, you can controlclick it and it'll lead you directly to that CSS directive. So you can see that for example, this size control applies a small medium to the text flex between the elements. BG neutral rounded full
Speaker A
some padding horizontal padding vertical and a gap in between the elements that actually creates this outer shell.
Speaker A
And then to each div within it, we apply a size of eight rounded full cursor pointer to make them appear clickable text black and flex center. So we know we can choose between those two versions. So again, at any point you're
Speaker A
wondering what something does, just go ahead, click on it, and you'll be able to see the full CSS for it. Finally below all of these divs, we'll render another P tag with a class name of text-white and text-4 XL that'll say render canvas
Speaker A
because now very soon we'll start rendering the canvas right here. Before we do that, we'll have to store a lot of different things within the state. And sure, you could use React context or you can use Zustand. Zestand is a super
Speaker A
simple store or global state setup for React applications. So you can install it by running mpm install zestand as well as clsx. We'll use this for some dynamic styling with tailwind CSS.
Speaker A
Once you install them, you can create a new file or rather a new folder within the source folder which we'll call store. And within the store, we'll create a new index.js file. Here we have to import create from zestand to be able
Speaker A
to create a new store. Then we'll create that store and we'll create it as a hook. use MacBook store. It's equal to create from zustad to which you pass a callback function that automatically returns an object. And here you can
Speaker A
define some global state variables such as a color of 2 e 2 c 2e. This is the color I picked from the design. And you can also provide a setter variable for that variable such as set color which
Speaker A
accepts a color and sets it within an object like this. Why are we doing this?
Speaker A
Well, to be able to keep track of the information of which color of the model is currently being shown. And we have to do the same thing for the scale such as scale of 0.08 set scale which will take in the scale
Speaker A
and set the scale. Finally, if we decide to reset it, we have to have a callback function that'll be doing just that which can simply call set and set the color to the default color of hash 2E2C2E.
Speaker A
And it'll also set a scale to 0.08. Perfect. And now this is our store that we'll export and we'll be able to use it all across our application. So now let's use those store values and update the color and the scale within the product
Speaker A
viewer. Head back over to the product viewer component and right at the top say const and then dstructure some of the values coming from use MacBook store just like this and make sure to import that use MacBook store from forward
Speaker A
slashstore. Now from it we can get the color the scale the set color and the set scale setters so that we don't have to manage that state over here we're managing it within a global state.
Speaker A
So all the logic will be right here whereas here we focus on the presentation. What you can do right now is in color control where we have this div you can give it an on click that'll be equal to a callback function where
Speaker A
you can set color and set it to the color that is being clicked. In this case it is hash a db 5bd make sure to call that as a function because that's how we've created this setter and then
Speaker A
this will set the color but we also have to keep track of that color within the class name. So I will put this into multiple lines. We have a div with not an on clock. This will be an on click.
Speaker A
And then for this class name we will call clsx. This is that package that we installed that allows us to set the base style such as bg neutral of 300. But then it also allows us to check whether
Speaker A
color variable is set to hashb5bd. In that case, we'll make this button appear active by giving it an active class. So now if you head over here, if you click on it, it now it gets the active class. And at the same time, the
Speaker A
color is also selected. What does that mean? Well, that means that now we can display the color right here. Even though it won't be in a human readable name,, but, at least, we, can, get, the variable. So if we say MacBook 16 in
Speaker A
color, you can see this color was about to be shown. And now if you duplicate this div right here and replace it with the one below, the only difference is that this one will set it to a dark
Speaker A
color of 2 E 2C2E with a BG neutral of 900. And if color is 2 E 2C2E, then it'll be active. So now we can see how we are modifying the value in the global state and we're changing the presentation based off of
Speaker A
which color had been selected. We can do absolutely the same thing for the scale.
Speaker A
So I'll duplicate this div once more and paste it right here instead of this div on this click. We will set the scale to 0.06 which is a bit smaller.
Speaker A
And for CLSX we will check if scale is triple equal to 0.06. In that case, we will apply a BG white and text black.
Speaker A
Else we will apply a bg transparent and text white to let it know which one had been clicked.
Speaker A
This div won't be self-closing. It'll actually render a p tag that'll render the size. And now I will duplicate this div for this 16. Change the name to 16 and change the scale to 0.08. 08 both here and here. So now you can
Speaker A
switch between different colors and we can also switch between different scales which will modify this variable. So now that means that we can use this scale variable from the state to also show it right here. Of course it's not showing
Speaker A
the actual numbers. We'll fix that later on., But, at least, we, know, that, our modifiers aren't working. And now we can start getting closer to displaying this big model right here in the middle. To display something in 3D, we have to
Speaker A
install packages that help us do just that. So run mpm install. The most important one is three as in 3JS. Then we need a helper library called a react-3/j which makes it easier to work with 3.js within a react environment which is
Speaker A
composed of these UI components. And then finally at react-3/fiber and then run enter. While that installs head back over to the product viewer.
Speaker A
And for now, we will render a canvas right here below all of these divs where we have this P tag simply by saying canvas. This, of course, has to be imported at the top coming from React 3 fiber. Give it an ID equal to canvas.
Speaker A
And if you save it, you won't be able to see anything within it. So, let's add a box coming from React 33. Here it is. You can play with that box by maybe modifying the position a bit such as
Speaker A
setting it to minus one, one, and zero which will move it across the space. In 3JS and in any 3D environments, we have three axes, X, Y, and Z. X is left and right. Y is top and bottom, and zero is
Speaker A
how far or how close the item is. If you increase the Z, you'll see that it'll get very close. So close, in fact, that maybe it'll jump off the screen. But if you make it smaller, you'll see that
Speaker A
it'll go further away. I always recommend that you play with those values and see what each one does. We can also modify the scale a bit by setting it 10 times the scale that we have chosen from our application. So you
Speaker A
can see now it'll actually modify the box size depending on what you click. And check this out as well. You can also apply a material dash color to it and set it to the color coming from our state. So now it's dark
Speaker A
and now it's white. And this box can be a self-closing component. And it appears like it's not properly centered, is it?
Speaker A
So we can give this canvas some camera properties such as a position of the camera not of the box to 0 to 5. We can also give it an FOV. This stands for field of view which I'll set to 50. I'll
Speaker A
set the near plane to 0.1 and far to 100. Now if you reload, you'll see that the camera angle will have changed. And you can continue playing with it until you have it perfectly centered. And the most important thing is to be able to
Speaker A
interact with it. For that, you need something known as orbit controls. And you can also give it a property of enable zoom and set it to false because we don't want to be able to zoom. We just want to be able to move it around.
Speaker A
Right now, it's either super light or very dark, so we cannot really see the edges of it. But later on, we will light up our scene and you'll be able to see it in more detail. Perfect. So now we
Speaker A
have created this section where we have a title, we have a canvas where we're displaying something and then we have controls where we can actually modify the global state and change the values so that the canvas interacts with what
Speaker A
we're changing. Now in the next lesson we can focus on rendering not just the box, but the actual 3D MacBook model.
Speaker A
Let's turn this cube into a MacBook. Thankfully, we already have a model for it within public models MacBook 14.GB.
Speaker A
This is a 3D file. But before we can use this 3D model inside React 3 fiber, we need to convert it into a React component. And for that, we'll use this gltf2.jsx package. What it does is it takes a 3D
Speaker A
model and just through the terminal, it converts it into a nice React component. So back within our terminal, navigate over into dot /public slashmodels.
Speaker A
Here you should have all the different models and then run mpxg gltfjsx macbook 14.gb capital T. This command will convert the MacBook 14 GB file into a React component and the - T flag will flatten the transformation giving us a cleaner
Speaker A
structure. If you press enter, it's going to ask you to install that package. So, press Y. Yes. And if it needs some additional permissions, you can add pseudo in front of it. But in this case, we're good. It looks like it
Speaker A
generated a new file. So, let's check it out within our application. It's this MacBook 14.jsx but now we want to move it within a new component that's going to be within the components folder. So within components create another folder and call it
Speaker A
models. This will be for our 3D models. And then simply drag and drop this model within the models components.
Speaker A
Here it is. And it's going to look something like this. Now we want to apply some changes to it such as renaming it from model to MacBook model 14. And we can also export it as a default function. So we can say export
Speaker A
default function MacBook model 14 because it's the only thing we'll be exporting from this file. Then we need to update the model path use gltf coming from forward/models/magbook 14t transformed.gb.
Speaker A
Let's make sure that exists by heading over to models magbook 14 transform.gb. It is here. And then we also want to apply a texture to this laptop by saying con texture is equal to use texture forward/screen.png.
Speaker A
This allows us to add some kind of a screen image over to this model. Then we have all of these different meshes or geometries that together make this model. The only mesh we need to change is going to be second to last one. This
Speaker A
one right here 123. I want to expand this one so it's not a self-closing div. And within this mesh we want to display the screen. Don't ask me how I know this. It's impossible to tell just by the numbers, but you had to
Speaker A
just render the model before and then see that under 123 that's actually the screen. I'll show you later on what happens when we change it. So here I'll render a mesh basic material which allows us to apply a map of the texture
Speaker A
coming from the screen image just like this. And then at the bottom you say use gltf preload forward slash and you have to change this to models MacBook 14 transform GB. Perfect. And now before we check it out let's quickly do the same
Speaker A
thing for the MacBook 16 code. We're still within the models folder within our terminal. Run mpxg gltfjsx MacBook 16.GB- capital T. Press enter and it'll very quickly generate a new component.
Speaker A
It's right here. So, you simply need to drag and drop it into source components models right next to version 14. As before, we'll have to make some small adjustments to it, such as modifying the path right here by saying forward
Speaker A
slashmodels slash and at the bottom forward slashmodels/magbook 16 transformed. And we can also use the texture by saying const texture is equal to use texture/screen.png.
Speaker A
Now we can use that texture right here within the 123 mesh which is actually the screen where we can apply a mesh basic material which basically allows us to render an image through a map of texture coming from our
Speaker A
screen.png. And one more time we want to follow the same process for the last model MacBook GB. We'll use this one later when we implement a section where the MacBook texture changes as we scroll to the bottom. So, as before, just run
Speaker A
this, but instead of saying 14 or 16 just render it like this. And then drag and drop this new model into the models folder. Don't forget to change the path to for/models as well as right here. And then later
Speaker A
on, we can change the texture. For now, let's put the two models, the 14 and 16 to use right here within our product viewer. Instead of rendering a box, we'll render a MacBook model 14.
Speaker A
Pass to it a scale equal to 0.06 and position equal to minus1 0 0 and self-close it. And make sure to import this model at the top from where we created it. You can pass any 3D props to the MacBook model because if you take
Speaker A
a look at it, it expects all the props and just passes it down to the 3D model.
Speaker A
Now, if you reload, you'll be able to see the model right here, but it's a bit dark. So, if we try adding some kind of an ambient light, maybe with an intensity of 0.5 that'll light up the keyboard a bit more
Speaker A
so it's not super dark. Maybe for the position, we can keep it zero so it's centered.
Speaker A
There we go. And if you increase the light a bit, like let's say to one you'll see that it refracts the light a bit differently. But, of course, we're still not quite there yet. The model is there and we can move it around. But
Speaker A
take a look at the final version. It is much more polished. Making it go from this to this has a lot to do with lighting it differently. So let's do that next.
Speaker A
Let there be light. We'll create a lights component that defines all the lighting for a 3D scene. It'll use a mix of environment lighting and spotlights to achieve an Appleike studio photography look. Go ahead and create a new file within the components folder
Speaker A
and call it studio lights.jsx. Run rafce and then go ahead and import it right here within our product viewer instead of the ambient light. If you do it just like that, it'll break because we're trying to render a div within a canvas
Speaker A
which doesn't work. Lights have to be parts of the canvas as well. So, I'll turn it from a div to a 3JS group with a name of lights. That on its own won't do anything, but it'll fix our view. And
Speaker A
within it, I'll create a new environment coming from React 3 Dre. This will wrap the scene with lighting. And if we give it a resolution of 256, it'll add subtle reflections to metallic and glossy surfaces. Resolution of 256 is balanced
Speaker A
for performance and detail. Within it let's render another group. And within that group, we can render something known as a light former coming from React 3 Dre. It's a self-closing component. This will simulate a large soft rectangular studio light. Apple
Speaker A
often uses these softbox lights to highlight product edges without harsh shadows. So let's give it some properties such as form of rectangular light, intensity of 10. Let's give it a position of minus 10, 5, and minus 5, a
Speaker A
scale of 10. And if you save it, you can see that it's kind of coming from the left direction, right? Yeah, it's coming from there definitely. If you change it over to 10, it'll be coming from the right. Or maybe far right. It's too far.
Speaker A
What if we bring it a bit closer? 20. Yeah, you can see how it's starting to light up a bit more. Perfect. We can also rotate it a bit by giving it a rotation dashy of math.
Speaker A
And yeah, you'll have to use a lot of radians and degrees in 3JS. And divide it by two. So we just give it some rotation. I will now duplicate this and create another light former with the same intensity but with a different
Speaker A
position. It'll be 10 0 1 coming from the right side. And this will now properly light up the entire model.
Speaker A
Beautiful. We could call it a day right here. But I also want to teach you how to add some spotlights. So create a new spotlight with an angle of about 0.15 a decay of zero intensity of math.py * 0.2. And now if you reload
Speaker A
you'll be able to see that there's going to be a new light coming in. Maybe we can remove this environment.
Speaker A
So you can see just the spotlights. It seems like it's coming right at this spot. If we increase the angle to maybe like one, you can see that it's much larger. But I'll bring it back to 0.15.
Speaker A
And I will position it a bit differently by giving it a position of minus2.
Speaker A
So you can see that this dot will now move off of the screen. I'll also give it 10 and five.
Speaker A
And now you can see how it's hitting the screen from the top. Pretty cool, right?
Speaker A
Now, let's duplicate it two more times. For the second one, I'll change the position to 0, minus 25, and 10. There we go. So, now it's coming from the bottom a bit. And also the last one I'll change the position to 0 155. And
Speaker A
now it's also coming from the sides. Again, you can play with this and come up with something that looks and feels the best for you. In the last one, I'll just change the decay to 0.1. And I'll increase the intensity from 0.2 to
Speaker A
three. So this way it'll shine even brighter. But again, feel free to modify it however you like. And now if we bring back our previous studio lightning alongside these spotlights it'll feel like a more complete look.
Speaker A
Maybe we can decrease the last spotlight just a tiny bit. And there we go. Take a look at this.
Speaker A
Beautiful. So now you get the idea that it's not only about the models themselves and the meshes or even displaying a specific texture on a mesh.
Speaker A
So this was the default. You can see how it wasn't high- res, but we ended up adding this more higher res texture on the screen to get something like this.
Speaker A
Hopefully that makes sense. But yeah it's not only about the meshes and geometries and materials. It is also about lights. Because if we now remove the studio light component, we don't have anything besides the screen. But if we bring it back, it looks great.
Speaker A
Perfect. Congrats on adding the lights. In the last lesson, so far, we've loaded up our 3D model and added the lights.
Speaker A
But now, we'll animate it so that when you move it around, it always returns to the center position. And you can also switch between different sizes. We'll do that using GSAP. So open up your terminal and run mpm install gap and
Speaker A
at@gap/react. Alongside that, we'll also need another small package called react responsive which is used to apply conditional animation logic and layout depending on different screen sizes that your users are on. So just install it.
Speaker A
After that, we need to register our gap plugin. We can register it within the app.jsx because that's our entire application. So, right at the top, just import scroll trigger coming from gap slall and also go ahead and import gap coming from GSAP. These
Speaker A
two imports are importing the main gap library as well as bringing in the scroll trigger or any other plugin like maybe split text that we'll be using throughout the application.
Speaker A
Scroll trigger will let us animate things based on the scroll position such as pinning, scrubbing, and triggering.
Speaker A
And yeah, Gab plugins aren't automatically active. You have to register them. So adding this single line gap.register register plugin and you can say scroll trigger right here.
Speaker A
Later on we might want to add a split text. Make sure that your plugin is globally accessible across your entire application. So putting it in app.jsx is a great choice. With gub installed we are ready to create a new file. And this
Speaker A
one will actually be a 3JS component. So under components I'll create a new folder and I'll call it three. Within three, I'll create a new file called model switcher.jsx which will handle the transitions between two different models. It'll be
Speaker A
responsible for rendering both MacBooks 14 and 16, detecting the size from the user UI, and animating transitions between the two models using GSAP. It'll also implement something known as presentation controls, which will allow users to rotate and inspect the MacBook in 3D.
Speaker A
So, first things first, before we even add anything into this model switcher let's go ahead and import it right here within our product viewer by saying import model switcher from three slashmod switcher.
Speaker A
Oh, and I just noticed studio lights are a threejs component as well. They're not a typical div. So, I'll also put the studio lights within three. And yeah make sure to also change the import once you import the model switcher. We will
Speaker A
render it right here within the canvas. That's the difference between regular components and 3JS components. 3JS are within the canvas and they can't be a typical div. They either have to be a mesh or a group. And instead of
Speaker A
rendering these basic orbit controls, I will now render a model switcher as a self-closing component. And we have to pass a prop of scale to it. Now we'll change the scale depending on the screen size we're on. For that, we'll use that
Speaker A
responsive package that we installed not that long ago. So I'll check if we are on mobile. And if that is the case, we can check it by using the use media query hook coming from React Responsive.
Speaker A
And the query that we'll check for is max width is 1,024 pixels or lower since we're using the max width. This means mobile or tablet. So if we are in mobile, we'll check that right here. And if that is the case, we'll provide a
Speaker A
scale of minus0.03. Else we'll provide a full scale. This will make it look better on mobile and tablet devices. So now let's go over to model switcher and let's develop its functionality. First things first, run rafce to create a new functional
Speaker A
component called model switcher. Then accept a prop of scale. Oh, and we can also accept the is mobile prop here. So let's go ahead and pass it over from our product viewer. Is mobile is equal to is mobile just so we don't have to call the
Speaker A
hook one more time. And alongside getting these two props, we can also create new references. Small MacBook ref is equal to use ref as well as a large MacBook ref is equal to use ref right here. Use ref
Speaker A
is obviously coming from React. So make sure to import it at the top. There we go. And now we'll have to choose when to show a big one or when to choose a small one depending on the scale. So we'll say
Speaker A
const, show show, large, MacBook, if, the scale is equal to 0.008 this will be set to true or if the scale is equal to 0.05 then this will be set to true as well.
Speaker A
Now within JSX I will render an empty react fragment and within it something known as presentation controls within them I'll render a group with a ref of large MacBook ref. And within that group, I will render a MacBook model 16
Speaker A
which we have to import from the right file. Let's try to find it within models. MacBook 16. Did we change the name right here? Oh, I forgot to rename it and to export it as default. That is very important. So, just do export
Speaker A
default function MacBook model 16. And you can do the same thing for this one. export default MacBook model. Perfect. So now if we head back you can now import that 16 version and to it you can pass the scale of if is
Speaker A
mobile. In that case you can pass a 0.05 scale else you can pass 0.08 scale. This is just to make sure it looks good on all different screen sizes.
Speaker A
I will now duplicate this presentation controls right below. will render the small MacBook Ref. The smaller one is the 14 model and the small is 0.03 on mobile and 0.06 on desktop. So now we can see so many models right here.
Speaker A
Definitely not what we want, right? But I think this just shows you that we no longer need just a singular model MacBook 14 right here in the canvas because now this model switcher will render different models depending on
Speaker A
what we click on. So to both of those let's pass and spread the controls config. This controls config will be an object where we can define how we want to move around this 3D model. Right now nothing is applied. And maybe instead of
Speaker A
showing both, let's just show one for now. So I'll comment out the other. So now you can see that you can move it around however you please and it stays that way. But you can give it a very
Speaker A
simple property of snap to true. What this will do is it'll snap back when released. So now if you move it around and you release, it goes back. So even if you mess with it a lot, it always
Speaker A
gets back to the frontal vision or to the general position. You can also pass the speed in which you can slide it around. Zoom is there as well. All of that works normally. Now there's a polar angle which will set to math.py
Speaker A
or minus math.py and math.py. Now what this does, you can think of these as vertical rotation limits. If you bring the polar back, you can actually slide below and see it. Whereas if you remove the polar and reload
Speaker A
you cannot go any more down, which maybe I even like because you'll never be looking at the bottom. You'll be looking at the sides. There's also the azimuth which is the horizontal scroll, which I'll set to minus infinity and infinity
Speaker A
which means that you can slide horizontally however much you want. There we go. And it'll always go back.
Speaker A
Let's see how many circles can we make it make. Oh, okay. Pretty cool that it doesn't rotate in as many circles. It just gets back one circle, right? Amazing.
Speaker A
And you can also set up some additional physics such as config is set to mass of one, tension of zero, and friction of 26.
Speaker A
What this does is it tries to replicate realw world physics as you move this device around with mass, tension, and friction. Now, let's create some helper functions right at the top of this model switcher that'll allow us to fade the
Speaker A
different meshes and move the different groups. I'll first outline some constants, such as the animation duration which I'll set to 1 second. Same for the offset distance. I'll set it to five.
Speaker A
This offset distance is how far one model will move off screen when it's hidden. I'll create my first utility function called fade meshes.
Speaker A
It'll accept a group and the opacity and it'll open up a function block where we first check if a group doesn't exist, we exit out of the function. We just return. But if a group is there, we want
Speaker A
to traverse all the elements within the group, get access to each of the children like this. And then for each one check if child has a mesh, then we can change the mesh of that material to transparent by setting it to true. And
Speaker A
then use the gap property.2 to on the child material to apply the opacity throughout a specific duration that will be set to animation duration. And this has to be within an object. GSAP of course has to be imported from GAP at
Speaker A
the top. And what we're doing here is we're setting the material to transparent at the start, but then we'll animate it slowly so that it looks like it's coming to life. Then we can do a similar thing for moving the element by saying move
Speaker A
group is accepting a group and the X. And this will allow us to animate the horizontal movement of a group. As before, we'll check if a group doesn't exist, then we'll return. But if it does, we'll use Gap to animate the group
Speaker A
position to the following X position over a following duration of animation duration. I'll collapse these two functions. And now we'll be able to use these two utility functions within something known as use Gap, which is a custom hook coming from Gap React
Speaker A
making it easier to work with Gap within JSX components. So right here above the controls config, I'll define the use GSA hook. And like use effect, we can make it run whenever something in the dependency array changes. In this case
Speaker A
whenever the scale changes. So right here, we want to animate the movement of a group from a large to a small MacBook.
Speaker A
Right now we're in 16. So I'll say small MacBook ref to get the current position. And we'll move it by the offset distance. So minus offset distance or move group large MacBook will move it to the position of
Speaker A
zero. Then we can also fade out the small Mac and fade in the large Mac as it is moving. So I'll say fade meshes.
Speaker A
I'll fade in the small MacBook refur to zero. And we will fade in the other one which is the large MacBook ref.
Speaker A
We'll fade it over to one. And we have to do the same thing, but if we're currently showing the small Mac. So I'll say if show large MacBook is true, then we want to run all of these.
Speaker A
But if we're currently showing the small MacBook, then we want to do it the other way around. So I will duplicate all of this, but we'll change the values to small magf.curren to zero. And the other one will move it by the offset distance.
Speaker A
And we want to change the opacity to small to one and large one to zero. So now if I save it and reload, it's currently on 16, but if I move over to 14, it slides away and a new one should
Speaker A
technically appear. Oh, and wait, it will as soon as I comment this one in because that's the 14 model. So, one more time, 16 slowly fades out to the right and 14 slides in from the left.
Speaker A
And you can see that now we have a full presentational controls over the two models that we can switch between and move around. Next up, we just have to change the color. To change the color we can head over into one model and then
Speaker A
the other and then apply that color. So head over into MacBook 14 models and then we'll have to extract that color from the global state by saying const color is equal to use MacBook store which you will have to import right here
Speaker A
at the top coming from store index.js or just coming from store. Then we'll also need to get the scene from our transformed GB model. And then we will use a use effect to apply that color across all of the different meshes. I'll
Speaker A
say use effect create a new callback function and run it whenever the color changes. In this case, it'll change whenever we change it right here. What we want to do then is call the scene and then traverse through
Speaker A
it. We then want to get access to each child from the scene and for each one we want to check if that child is a mesh.
Speaker A
And then here we want to change the color only if the part name is not in this list that I called no change parts.
Speaker A
So what is this no change parts? Well it'll come from constants. So head over into constants index.js JS and alongside the snav links, we can also export and create no change parts and this will be an array. Just so we don't have to write
Speaker A
it down manually, you can find it within the video kit down below. If you scroll down under snippets to copy, you can copy the entire constants index.js and override what we have right here. You'll notice that here I created an array of
Speaker A
different names to which I do not want to apply the color. I manually went through every single mesh and I checked whether that color is an outer part of a MacBook. If it is, we want to change its
Speaker A
color. But if it's a display or something else, then we don't want to touch it. So now that we have this no change parts and now that we're exporting it from the constants, we can now import it right here and say if
Speaker A
not no change parts. the child name. In that case, we want to change the child domaterial color to be equal to new three doc color and want to apply the color that we're passing right here from the state. Now, before you
Speaker A
save it, make sure to import three by saying import everything as three coming from three. Or in this case, we're just using the color functionality. So you can say import color from three and then you can do it like this. Now if you want
Speaker A
to test it out, we need to switch over to the size that we made these color changes on. Right now we've done the changes for the MacBook model 14. So if you switch over to 14 and now test it
Speaker A
out between light and dark, you'll be able to see the difference. Let's make it work for the larger model as well by copying this part entirely.
Speaker A
Heading over to 16 and then just pasting it over here. Make sure to import the color from the MacBook store. Get the nodes, materials, and the scene coming from MacBook 16 this time and then get the texture.
Speaker A
After that, we apply the use effect across all the different meshes, but only the specific ones. And this means that we can now change the color for both MacBook sizes. Now that that works we can now remove this piece of text
Speaker A
right here that is right here within this product viewer. And we can now just say MacBook Pro available in 14 and 16in variants in space gray and dark colors. Perfect. You can keep it like it is or if you want to, you can totally
Speaker A
remove that piece of text and just keep the switcher. Perfect. And of course keep in mind that this looks so much better on a larger screen where these colors and sizes can truly shine. Now with all of those changes implemented
Speaker A
we want to run a new code rabbit session to see what it has to say about our code. Now, now it has four additions seven modifications, many new lines added. So, let's hit enter to hop into the review. Hopefully there are no
Speaker A
ghosts of bugs pasts. Let's wait a bit more and see if AI really does know more than us. And there we go. As the code rabbit is reviewing our code, we can already see two potential issues that we
Speaker A
can then expand. One problem is that we can avoid exact equality comparison with floatingoint scale values. If you press enter on that toggle, you'll be able to see more info about it. It says here that using triple equality with 0.08 08
Speaker A
is fragile because floatingpoint arithmetic can introduce precision errors. If the scale value comes from calculations, animations or user interactions, exact equality may fail unexpectedly. So, it's always better to use thresholds or if we know for a fact that these points are going to be
Speaker A
exactly as they are, and we do know that because we have set them up, then it's better to define those scales as constants. And now we have a couple of options. We can either close the details, copy the prompt to fix this
Speaker A
with AI or apply the suggestion or reject the comment. In this case, the change is super simple. So I can go ahead and implement it manually by copying these couple of lines. Heading over to the model switcher and then
Speaker A
right here at the top, we can define different scales and then just modify this line to use these new constants.
Speaker A
With that, our code is more predictable and descriptive. Going back to the terminal, let's now collapse this issue because we fixed it. And let's head over into the first issue right here where it says that we need to add a missing
Speaker A
dependency to the use effect. The scene object is used inside the use effect but not included in the dependency array. If the scene reference changes on model reload, the effect won't rerun with the current color. That is actually a super
Speaker A
nice suggestion. And I just press the letter A and it seems like it fixed it on its own. Let's head over to the model 16. And there we go. The scene got added. That's great. We can also do it
Speaker A
for the model 14 where we can add the scene. Now we can head over to the next issue within our index. Here we have some inconsistencies within the text.
Speaker A
This one for sure I will automatically fix by pressing the letter A. And then finally the color typo in the reset function. It resets the color to 2 E2C3E but the initial color is 2 E2C2E.
Speaker A
So it's a bit different. So, let's go ahead and autofix it. If you head over to the store, this actually shows you that you can completely change the color of the model if you want to. So, if you
Speaker A
want to change it to something entirely else, you can do that. Like this is a nice greenish color. And you can see how differently this MacBook looks like. You can go with something much different.
Speaker A
Let's go with 37s. And this actually gives us a nice grayish color. Or if you want to go with something darker, this works, too. So, if you want to add multiple colors, feel free to do that.
Speaker A
This was a great catch and definitely not something I would have noticed on my own. So, now we can go back to home. And with that, all of the suggestions had been implemented, which means that we're finally ready to push our code to GitHub
Speaker A
by running git add dot get commit-m implement 3D product viewer section and get push. And just like that, our latest changes have been pushed over to GitHub. In the next lesson, when we implement the next section, I'll show
Speaker A
you how you can also use Code Rabbit when we open up a PR from a new branch.
Speaker A
Code Rabbit CLI is great for getting the fixes directly within your IDE. But where it truly shines is within PRs.
Speaker A
Now, the next section we'll implement is this amazing video playback that shows you the M4 chip, allowing you to play those demanding games on that powerful MacBook. So, since this section will be a bigger one and for sure we'll need a
Speaker A
PR review when we finish it, let's go ahead and immediately create a new branch by running get checkout-b and call it showcase.
Speaker A
This will automatically switch you to a new branch so we can dive right into it.
Speaker A
Okay, here's the hero section. Here's the 3D model viewer. And then right below it, we'll have a showcase section.
Speaker A
a section that opens up a full screen video on top of which we'll display an overlay that will show the user what powers this game and that is a very powerful Apple M4 chip and then we of course show some more info about it. So
Speaker A
let's get it developed. I'll start by creating a new component within the components folder which I'll call showcase.jsx.
Speaker A
I'll then run rafce to quickly spin up this new showcase component and then we can import it within app.jsx right below the product viewer. There we go. Here is the showcase. Heading right into it, we can start developing its UI by turning
Speaker A
this div into a section that has an ID of showcase. So we can actually scroll to it from the navbar. Then within it I'll render a div with a class name equal to media because within here, we'll play this
Speaker A
video on repeat. We'll use a regular HTML 5 video element that'll have a source of forward slash videos/game.mpp4.
Speaker A
So, as soon as you do this, you'll be able to see what appears to be a photo but is actually a video. And since this section is very visual, I want to expand it a bit so we can better see what's
Speaker A
happening. I'll set it to loop. I'll make it muted and I'll make it autoplay as well as plays in line. That way we don't have any kind of controls to scrub the video. Rather, we'll play and pause the video depending on the scroll.
Speaker A
That's where GE comes in. So now if you reload, you'll be able to see this beautiful video which has to be high- res because it's showing on full screen.
Speaker A
Now with that in mind, I want to apply a mask on top of that video. So right below this video element, create a new div that has a class name equal to mask and within it a self-closing image with
Speaker A
a source of mask- logo. SVG. So typically whenever you are applying masks, you're using SVGs because they're super easy to show on top of other content. Right now that mask is dark and it is right here below. So you can see
Speaker A
how it looks like if I click and hold and then drag and drop it over it. We want to align this and make it cover the full screen and make it show as we scroll. But while we're here, let's also
Speaker A
pause this video for a second just so it doesn't continue scrolling. And let's implement the section below the video.
Speaker A
So we'll go below these two divs and create a new div with a class name of content. Another div right here with a class name of wrapper. And then another div right here with a class name of on
Speaker A
large devices max WMD. That way it won't take the full width of the screen.
Speaker A
Rather, it'll stay in the middle. I'll give it an H2 and I'll call it rocket chip. It's super fast. Then below it I'll render a div with a class name of space-y-5 margin top of seven to divide it a bit
Speaker A
and padding e p that's inline end 10. Now within it we can display another p tag and say introducing then we can put a space. Then still within this P tag but now within a span that'll have a
Speaker A
class name of text-white we'll display M4 the next generation of Apple silicon. And then after the span I'll say M4 powers. Then continuing with the rest of the UI we can create another P tag right here. And we can compare it with the
Speaker A
final website. If you scroll down you'll see that here text is much bigger. And I'll copy both of these paragraphs. I'll start with the first one.
Speaker A
And then I'll create another P tag below it. And I'll paste the second one.
Speaker A
Finally, below that second P tag, we'll need to create another P tag, which could technically be an anchor tag with a class name of text- primary. And within it, I'll say learn more about Apple Intelligence. Finally, we just
Speaker A
have to create these two small sections up to four times faster pro rendering performance than M2. So, let's do that by heading two divs down one and two.
Speaker A
Create another div to encapsulate that section with a class name equal to max w of 3xs and space-y and then two more divs each for its own section.
Speaker A
one with a class name of space dashy dash two to create some space in between the elements and then a p tag that says up to below it an h3 that says 4x faster and then below it another p tag that
Speaker A
says pro rendering performance than m2 and we can say four times faster pro rendering performance this is good now we can duplicate this just below and for this one I'll say up to 1.5 times faster CPU performance than M2. So now if we head back over to
Speaker A
our current application, it'll look something like this. This is just plain structure. It's plain text and elements that we can then animate. So let's start with implementing this stunning video mask effect. Right at the top of this file, I'll first get access to the is
Speaker A
tablet variable and this will be equal to use media query. Remember that React responsive package we installed which allows us to figure out on which device are we loading this application. For this one, we want to know whether it's
Speaker A
tablet. So we'll trigger it when the max width is smaller than 1,024 pixels. And now we can put gub to use by using the use gub hook within which we're going to check if we're not on tablet. Then we
Speaker A
can start applying the animation. Of course, we have to keep track of the changes in the is tablet state and every animation starts with a timeline. We need to move something through time. So say con timeline is equal to gap which
Speaker A
you have to import right here at the top by saying import gap from gap timeline.
Speaker A
Then to it you can pass some additional options such as scroll trigger. We want to trigger this animation or this timeline not on click but rather when we reach the showcase section.
Speaker A
Specifically, we want to start it when the top of the screen reaches the top of the section and we want to end it when the bottom of the screen reaches the top of the section. We want to apply a scrub
Speaker A
set to true. So, we're actually animating the timeline with our scroll. And we'll also pin it. So now that we have this timeline, we can call it by saying timeline.2.m mask img. So we're now animating something within that timeline. In this
Speaker A
case, we're targeting this div image element. And you can define what you want to modify for that element. In this case, we want to transform it. So I'll run transform. And the transformation we want to apply here is a scale of 1.1. So
Speaker A
we're going to enlarge this image. And I'll also apply another dot2 function in this case to the content element where I'll set the opacity to one y position to zero with an ease of power 1.in.
Speaker A
Then I will autoplay the video. And now before we go ahead and test it out, I just noticed that I made a small mistake right here. The ID here should be showcase. If you fix that, all the contents within it will show more nicely
Speaker A
on the screen. And on smaller devices this won't scrub on scroll, but immediately the mask will be on top of the video. But now, check this out. If we enlarge the screen, reload, and start scrolling down, you'll notice that now
Speaker A
you'll see the full game, and you'll be like, "What's happening? We are looking at Assassin's Creed here." But as you continue scrolling, it'll all start making sense because this game can actually run on your M4 rocket chip.
Speaker A
Yep, that's the newest generation of Apple silicon. Pretty cool, right? It's such an effective section that nicely complements the first two sections we've created. So with that in mind, let's go ahead and commit it by running git add
Speaker A
dot getit commit-m showcase section and get push. Since we created a new local branch, you also have to set it as a remote branch. You can do that by simply copying this get push set upstream command. And then back on
Speaker A
GitHub, you'll see that the showcase branch had a recent push about 24 seconds ago. So you can compare it and open up a pull request. In production apps, you never push straight to main.
Speaker A
You implement the features on your feature branches and then open up a pull request. And that's exactly what I want to teach you right now. So create a pull request. And if you go under files changed, you'll notice that we haven't
Speaker A
really pushed the right changes. Back within our application, it seems like I was inside the models folder. And you always want to be within the root of your application when you're pushing it over to GitHub. So once again run git
Speaker A
add dot g get commit-m implement showcase section and then git push. This will now push the right changes over to GitHub with 11 file changes and it'll give code rabbit plenty of code to review. And it looks like it already hopped on to do that. It
Speaker A
may take a few minutes. So let's give this rabbit some time. We'll have to trust its mommy saying that it's the best code reviewer out there. Okay first things first. Code Rabbit provided this nice summary where it says that we
Speaker A
implemented a couple of new features. We introduced an interactive 3D product viewer with real-time color and size controls, responsive layout, and smooth model transition. And we added a scroll animated showcase section with pinned media and content reveals for an
Speaker A
immersive viewing experience. Okay, so let's see the comments it came up with. There's a couple of nitpick comments which are very small and you don't have to necessarily fix them, such as fixing placeholder links in the footer or
Speaker A
removing unused imports or maybe memorizing the color so we don't have to create a new one every time. But on top of these nitpick comments, there's also some major ones. And what I like about Code Rabbit is that it provides you with
Speaker A
a complete analysis chain on how it came up with this review suggestion. is suggesting to use sRGB color space so UI imagery isn't washed out and it even provided an alternative if we're using an older 3JS version. Now, here are two
Speaker A
super easy ways in which we can immediately implement these suggestions within our code. One is the commitable suggestion where you can review the snippet to be added and you can automatically commit it to the code. The other one is a prompt for an AI agent to
Speaker A
fix it for you. For this one, let's go ahead and commit it automatically by just saying commit and commit changes.
Speaker A
There we go. Suggestion applied. And it's, going to, update, itself, so, that, it's resolved. This was within MacBook 16. So if you head back over to your application and run get pool, you'll be able to see that there's going to be a
Speaker A
new pool on MacBook 16 with the implemented change. So head over to MacBook 16 and you can see that now it's using this sRGB color space. But of course always make sure to check what you're autoimp importing. In this case
Speaker A
the imports have to go above right here. And then inside of the component we are using this texture and applying a color space to the texture. And then we have the rest of the code. And if you take a
Speaker A
look at the screen right here, it's using the sRGB color space, whereas the V14 isn't. And if you're still not sure what these two lines do exactly, select them, press command shiftB, and type Juny.
Speaker A
If you don't have Juny installed, go ahead and install it by clicking the link in the description. But if you do you can just ask it, what do these two lines do? And just like that, we got a
Speaker A
short answer. Texture color space sRGB color space tells 3JS that the pixels in screen PNG are in sRGB space. So they should be gamma decoded on upload and displayed with the correct color management. No washed out or overly dark
Speaker A
colors. And the needs update flags that the textures parameters changed. So 3JS should re-upload texture to the GPU on the next render tick. Otherwise the color space may not take effect immediately. And now if you need a deeper explanation, it gives it to you
Speaker A
here. But in simple words, we're doing this so that the color on the screen isn't washed out or using overly dark colors. This is a nice catch by Code Rabbit. So let's go ahead and apply it to MacBook 14 as well by copying these
Speaker A
two lines, heading over to MacBook 14 and just pasting them right here below the texture. And make sure to import the sRGB color space. Now, back to Code Rabbit's review. It has something to do with the use effect in which we change
Speaker A
the color of the meshes within the MacBook model. It says that this is a major issue where we need to clone the per mesh materials before recoloring to avoid cross part bleed. Shared gltf materials will recolor excluded parts if
Speaker A
reused. Clone once and then set the color. Okay, that's nice. We immediately get a commitable solution. But before we commit, let's also add and push the changes we have within our code. just so we don't have any conflicts by running
Speaker A
get add dot get commit-m add sRGB color space and get push but it nicely noticed that the diff is outdated so we need to refresh and then try again there we go but in this case I believe that this
Speaker A
change isn't necessary and rather if we implement this the color switching on our MacBooks will stop working so we won't proceed with this suggestion there's another suggestion saying that we shouldn't pass both the material prompt and the child material because
Speaker A
the child won't reliably override. And I think this is true. This has to do with changing our screen where we're applying the texture of what's being shown in the screen. But here we're also applying an additional material. And here we're
Speaker A
adding a child material. So if I remove this material on the outside and head back, everything is still working as it did before. But now we don't have to be scared that it'll mistakenly render just this material instead of the child
Speaker A
material which is the actual screen. So I'll remove this material from the parent both on version 14 and version 16.
Speaker A
Perfect. Let's see what else we have. We have the constants here saying that we can add CSS definitions for box one and box two. No matching selectors found.
Speaker A
No, they should be there within the index.css file. So I believe with those changes implemented, we're good to go.
Speaker A
But let's just not forget to commit our latest change by running get add dot get commit-m fix material override and get push. With that done, all the checks have passed and we are ready to merge this PR into the main branch.
Speaker A
Perfect. That's it. Oh, and while I was reviewing this PR, Code Rabbit also gave me a complete walkthrough of what we're implementing here. It split the changes by cohort and gave us a summary for each one of the files or the cohorts of
Speaker A
files. For example, we have these 3D models and sure we know what those are.
Speaker A
But if somebody else is reviewing our codebase, it's super useful to get a very quick answer that these are glTF components and through them we can apply stored driven colors across meshes. Then the model switcher is toggling between
Speaker A
the large and small MacBook. And then we're using GAP to move those around. And check this out, there's even a sequence diagram where we can see how a user can interact with their app.
Speaker A
The user first loads the app. Then we render the product viewer which then reads from the store the color and the scale. The user can then change the color and the scale using the setter function. They set it, we update it, we
Speaker A
apply colors, and finally we animate the positions using Gab. It's so nice to have a visual representation of what we just implemented. And then here's the part for the showcase. I would have thought that it's impossible to show
Speaker A
this using a diagram, but hey, you render it, we register the scroll trigger, we pin it to the screen, and then as we scroll or scrub the timeline then the mask scales and the content fades and translates. Once again, it's
Speaker A
super nice to see this visually. And at the end of the day, we're even getting a poem. This is not just autogenerated but rather it's connected with what this PR just did. 3JS dreams and silver sheen. GSAP wins that softly glide. I
Speaker A
flip my lid. Colors bloom. A hop, a click, a state renewed. Showcase and viewer imbued. Okay. Well, that's quite something. So, with that in mind, the changes are now here. These great sections have been implemented. And now we can move to the next section.
Speaker A
Next, we have this next level graphics performance where many different screens will reveal the MacBook in the middle.
Speaker A
So, back within our code, let's first run get checkout and move to the main branch and pull all the latest changes.
Speaker A
Then, we can create another branch either for the next section or we can create one for sections to come. In this case, I'll do that by running get checkout-b and I'll call it finalize landing page.
Speaker A
Now that we switched over to that branch, we are ready to get started with implementing it. Just below this rocket chip, we'll focus on implementing the performance section next. So within the components folder, create a new file and
Speaker A
call it performance.jsx. And you can also use rafce to quickly spin it up. Then back within the app.jsx, you can just render it below the showcase and call it performance. As a matter of fact, we can create all the
Speaker A
other components that we'll also be using throughout the rest of this application. The next one will be called features. So let's implement a new file and let's call it features.jsx.
Speaker A
You can also run rafce to quickly spin it up. Import it within the app. Then we'll have a highlights section.jsx JSX where you can run rafce and also import it within the app. And finally, we'll have the footer. That one
Speaker A
is simple. So, just create a new file call it footer.jsx run rafce and then import it within the app.
Speaker A
Perfect. Four more sections to go. If you now start scrolling down, you'll see four different pieces of text, one for each of the sections. So let's move over onto the next one which is performance and let's start implementing the UI.
Speaker A
I'll first turn this div into a section and give it an ID equal to performance so that we can scroll to it later on.
Speaker A
Then I'll create a new H2 and just copy what the official website says next level graphics performance game on.
Speaker A
Below that we want to render all of these images. We can do that by creating a new div which you can call wrapper because it'll wrap those images and within it we can map over those images. I already stored
Speaker A
them within the public folder and mapped over them within the constants index.js. So you can just import them from there and you can see that each image has its own ID and the source. So now you can just run map for each image.
Speaker A
You can automatically return an image element with a source equal to image src an al tag of id and we can also give it a key equal to image do ID and since we're mentioning image source and ID too many times we can just
Speaker A
dstructure it right here by putting it in curly braces and that way you don't have to repeat yourself and say image dot every time.
Speaker A
Now, if you do that and head over to desktop, you'll see all of these images one by one being rendered right here.
Speaker A
Below those images, we want to display some content. So, I'll run a div with a class name equal to content and a p tag here. We want to display some text such as this one right here. And specifically, we can highlight one
Speaker A
sentence, one that says that gaming feels more immersive and realistic than ever. So that one I will put within a span element that has a class name of text dash white. So let's nicely enclose it within this span element. And if you do that
Speaker A
you should be able to see the text somewhere here. if it's not hidden by the images. And we also have to create an extra space right here before the span and then after the span to connect it with the rest of the text. Now we'll
Speaker A
animate this text and the images. But for this section, I want to try something different. I wrote a prompt and I want to try to give it to Juny to see how well it does. So just press commandshiftb and open up Juny. We can
Speaker A
start a new session and this time we'll allow it to code. I'll give it access to a specific file as the context. In this case, we're working on the performance section. And I'll paste the prompt that I wrote. I will leave this prompt as
Speaker A
well as the final output in the video key down below. So, you can test it on your end. And if something doesn't work you can always just use the final code.
Speaker A
Now, let's go through it. Juny, I need some gap animations added to the performance section. Here's what I'm after. The paragraph inside the content should fade in and move up slightly as it scrolls into the view. On desktop, I
Speaker A
also want the performance images to animate into their positions using a scrubbed scroll timeline. Each image should be targeted by its ID class and their final positions should come from the constants. On mobile, only the text animation should run, no image timeline.
Speaker A
Use GSAP with the section ref for scoping and so on. The end result should be a smooth text fade slide as you scroll in the desktop. The images settle into place in sync with the scroll. So let's see how well Juny does. Before it
Speaker A
starts writing the code for you, it actually goes through the project and tries to understand what's happening.
Speaker A
It's opening up index and CSS files just to get a better understanding of the codebase. And only when it reviews it it then updates the task progress because now it knows exactly which images we're talking about, what are
Speaker A
their final positions, and it's diving into implementing the animation. So it's editing the performance file as we speak. You can review all the changes in real time. And now, it's including the triggers. I'll give it some more time to
Speaker A
finish it before we check it out. And there we go. In real time, as I was speaking, it implemented GEP animations for the performance section with scoped section ref. So now if you open up the performance section, you'll see that
Speaker A
right at the top of the component we implemented, we now also have this use gub hook. In this case, we don't have to register the scroll trigger plugin because we've already done that in the app. But other than that, let's see how
Speaker A
well it implemented the animation. Oh and we do need the scroll trigger import after all to refresh the resize. So we can leave the import right at the top.
Speaker A
Now, if I get back to the code and I start scrolling down, you'll see that it actually slides all of those images very nicely. It's still not yet perfect as one of those images is the actual MacBook, which should remain in the
Speaker A
middle so that the final application looks something like this. But I got to say, Juny got very close with the animation. Now, let's go through it together and see where we can tweak some things. I'll first use this is mobile
Speaker A
ref by using the use media query so that we know whether we are on a tablet device or not by applying a query of max width of about 1,24 pixels. Then right here we're animating the text. We're targeting the P element within the
Speaker A
content and modifying its position. In case the only thing I wanted to do is to just modify the opacity and move it up a bit. So I'll make it start from the opacity of zero and the Y position of 10
Speaker A
and then change the opacity to one and the Y position to zero with an ease of power to out the duration can remain the trigger can be content P. So when the top of that elements enters the bottom of the view and we'll end it when
Speaker A
the top of that element enters the center of the view. And no need for these toggle actions. Rather, I'll just set the scrub to true, which means that it'll animate it as we scroll. And that is our text animation. So now, if you
Speaker A
reload and scroll down, you'll notice that the text actually scrolls in with the images. Perfect. Now, let's look into the animation for the images. We want to make sure this is desktop only.
Speaker A
So instead of doing this match media which is indeed a built-in GSAP feature we'll use that handy package by saying if is mobile return meaning don't apply any further animations. But if we're on desktop we'll create a new timeline
Speaker A
TL by saying GAP. Oh, it's already being created right here. That's good. And then for it we'll apply some defaults such as ease of power one in out. We'll apply a duration of 2 seconds and a override set to auto. This
Speaker A
will automatically handle the conflicting animations. For the scroll triggers, we'll use the entire section as the trigger. We'll start the animation when section top hits the bottom of the viewport and we'll end the animation when the bottom of the section
Speaker A
hits the top of the viewport. We'll set the scrub to one, which will sync the animation with the scroll direct sync and we'll invalidate on refresh, which means that GSAP will recalculate trigger points whenever a screen gets resized.
Speaker A
And now we need to animate each of the images to their final positions. But we don't want to do it for P5 because P5 is the MacBook image. If you head over to public and then see the image five
Speaker A
you'll see that that is the actual MacBook. So that one we don't want to animate.
Speaker A
So right here I'll check if item or position id is equal to p5 then just return skip the animation. If you do this and if we end everything properly let's see whether we have closed everything. Well I will remove this
Speaker A
bottom part for now just to make sure we're closing it properly. Gab starts here ends here and then we'll add this scope and section ref later on. There we go. Now it's good. So if I scroll down you'll now notice that
Speaker A
the MacBook stays in the middle. So nothing is being applied on it. We just have to fix the animations a bit. So let's fix those positions. First, I will remove this initial offset. If you do that, you'll notice that there's no
Speaker A
lowered opacity. All of them are at their full opacity. And then we're checking for different positions. We're checking if each one of these images has its position. like let's say ID with a P1 has a left of five and a bottom of
Speaker A
65. That's the ending position we want to apply to this element. So that's why we say TL as in timeline dot to this specific class name that we target. Then we apply the two variables. So that's this object that starts with Y0 and auto
Speaker A
alpha to one. But we can also start it from a basic object. And then for each one of these, if a left position exists then we move it that much percent to the left. If a right one exists or if the
Speaker A
bottom one exists, we do the same thing. And finally, if the transformation exists, we apply the transformations.
Speaker A
Then finally, we apply all of those Gap animations to that specific element. In this case, we can remove this return and instead to this use gub hook, we can give some additional options such as the scope of section ref, which means that
Speaker A
we're limiting gub queries to work only within this component as well as we want to add dependencies. So whenever the is mobile changes, we want to recalculate the positions. So now if you reload and go back and if you scroll down, you'll
Speaker A
notice that now these images will nicely reveal the MacBook. But you can notice that after we pass this section, they continue getting farther away. But they should actually get farther away as soon as we are looking at the screen
Speaker A
directly. And to do that, we need to change this part right here where we'll say that we'll end the animation when the center of the section hits the center of the viewport. So now if you reload, check this out. If I scroll
Speaker A
down, the full animation will play out by the time when we hit the center and all of the images will be that much farther away, revealing the MacBook in the middle. And then as I continue scrolling, they won't be moving anymore
Speaker A
but rather the text will be revealing itself at the bottom. This is beautiful. So Juny definitely helped us a lot with coming up with this animation, but we also needed to do some work on our end to tweak the animations to come up with
Speaker A
that real human touch. With that in mind, this section is done. And if you check it out on a tablet device and reload, you'll see that it also looks good. Well, you could tweak the positions of the screens outside to work
Speaker A
good on smaller devices as well. But if you go to something like a mobile or tablet, it still looks like there's a lot happening, right? You can do so many things on this MacBook device.
Speaker A
Again, if you want to make a bit more precise circle of apps around the MacBook, you can just head over into constants and then animate them to different positions depending on the screen size. You would maybe go something like this desktop and then you
Speaker A
would put the left and bottom within desktop. And you can also apply different positions for mobile or for tablet which would make it look great on all sorts of different devices. And then you can animate it over within that gub
Speaker A
section that we just implemented. With that in mind, we can check it out with code rabbit by just running the code rabbit command and then pressing enter to see what it has to say about our changes. Looks like we only got two
Speaker A
issues. One is in the footer and we should be using the semantic footer element. Yep, we'll change that later on. And then the second one is in the performance component where we need to use the alt attributes for the images
Speaker A
accessibility which I entirely agree with. So we can just press the letter a which will automatically apply the suggestion. Good. With that in mind, we are now ready to commit the changes by running git add dot git commit-m
Speaker A
implement performance section and get push. Since it's a new branch we have to set the upstream so we push it over to our remote repo. We won't be checking out the PR just yet. We'll first implement the other sections to
Speaker A
come and then we'll do it at the end as one final PR check. Next up, see it all in a new light section, which will be the feature section of our landing page. We'll have the same MacBook model as before, but
Speaker A
this time with an animated video screen. And then as you scroll, not only will we display additional features on the left and the right, but we will also rotate the MacBook to show in video on the screen what that feature is all about.
Speaker A
This is an amazing use case and combination of both Gap and 3JS. So let's do it together. Back within our code, now we want to navigate over to components and features. This is where we can turn it into a section that has
Speaker A
an ID of features and add an H2 that will simply say see it all in a new light like on the design. But before we go ahead and display it, there are two things we have to do. First, we have to
Speaker A
head over into our store. That's store index.js. And here we will now be modifying the texture of the screen as we scroll. So we have to change it in the global state. For that reason I'll choose the current texture and we'll start with it
Speaker A
being the videos feature number one.mpp4. But then we also have to have a setter function such as set texture that allow us to change what's being displayed on the screen as we scroll. And also when we reset, we can reset the texture to be
Speaker A
just the initial video. Perfect. Now let's go to the previously generated MacBook model. It's right here within models MacBook. Not the 14, not the 16 but just the regular MacBook. And here we want to add a support for the video
Speaker A
texture. We can do that by extracting the color and the texture which is the video coming from or is equal to use MacBook store.
Speaker A
Then from this use gltf model we also want to get access to the scene and make sure that the path is right. It's magbook-transformed.gp.
Speaker A
Once that is done you can say const screen is equal to use video texture.
Speaker A
This is a very nice utility function provided to us by React 3 Dre to which you can just pass a video. In this case it's texture because the path right here is coming from the store pointing to an MP4 file.
Speaker A
Then we want to do the same thing as we've done in the previous MacBooks and that is just to change the color of it.
Speaker A
So I'll copy this use effect and just paste it below where we'll have the scene. check if it's a mesh and if it is we will change the color and then of course if you head over to the mesh 123
Speaker A
geometry we have to remove the screen material and rather make it accept the child which will be the actual mesh basic material that'll show the screen texture or in this case it'll be just the screen I believe that's how we
Speaker A
called it right here at the top and that's it this now means that as before we rendered a texture texture that was an image. Now we're passing over a texture that is a video and that video is coming from a store and we can modify
Speaker A
it as we scroll. So let's develop it by heading over into features and creating a canvas within which we can display this 3D model. This canvas is coming from React 3 fiber. It'll have an ID equal to F canvas as in features canvas.
Speaker A
And we can immediately play with the camera position of the element will show in there. For now, I'll just leave it blank. Pretty cool thing is that we already created a component called studio lights which automatically lights up our scene. And we can also apply an
Speaker A
additional ambient light of an intensity of 0.5. And then here below we have to render our 3D model. Before we do that let's just display a list of features that we want to display below the canvas. So I'll render a div. And that
Speaker A
div will have a class name equal to absolute because it's going to show outside of other elements in the screen and insert zero. Within it, we can map over our features coming from constants.
Speaker A
And then for each feature we can automatically return a div with a class name equal to it'll be a dynamic class name. So we have to import clsx and it'll always have a class name of box but we also want to apply an
Speaker A
additional class name depending on the index of the box we're rendering. So I'll get the index and I will say dynamically give it a box and then index + one. So box one, box two, three and so on. And then also we want to apply some
Speaker A
additional styles by saying feature styles because within constants to each one of these images, we're positioning it in a different part of the screen.
Speaker A
You can see that right here. Email is on top left, image is on top right. Then we have middle summarize AI and at the bottom airdrop. These features won't show on the screen just yet because we have to animate them into place. So for
Speaker A
now, I'll just render a piece right here that says feature.ext. Even though it won't show yet, it'll show when we animate it in. So now we have to implement this 3D model. And to do that, I'll create a new component
Speaker A
right here on top of our features. And I'll call it model scroll because it'll be a 3D model that well scrolls and it's going to be a React component on its own. Within the return part of that component, we can just return a three
Speaker A
group with a ref equal to group ref. And that's a ref that we can create right here at the top by saying const group ref is equal to use ref coming from react starting as a null value. Then I
Speaker A
will load it within a suspense. That means that we can display some loading as it actually loads up because here we're displaying a video component and it does need some time to load. instead of just a regular fullback that says
Speaker A
loading, we can now display HTML elements within a 3JS canvas. So for that reason, we have to give it a fallback and wrap it in HTML. That then allows us to render some HTML elements within a 3JS canvas. In this case, I'll
Speaker A
just return an H1 with a class name equal to text-white text-3XL and uppercase. And I'll simply make it say loading dot dot dot. Now, once the loading is finished, we want to display a MacBook model, the one we created with
Speaker A
a scale of and now depending on whether it's mobile or not, we want to give it a different scale as before. So we can get access to this is mobile and make it equal to use media query.
Speaker A
And the media query will use is a typical tablet one. So that'll be a query of max width of 1,024 pixels.
Speaker A
And now if we are on mobile in that case we'll give it a scale of 0.05 a bit smaller else 0.08. 08. And we can also modify its position where it displays on the screen. It'll be 0 minus one and
Speaker A
zero. So we'll move it a bit down on the yaxis. And now we can render this model scroll right here within the canvas. If you do that, you'll see this loading that I was telling you about and then a
Speaker A
new MacBook that'll load with the video number one playing. But as we scroll nothing will happen.
Speaker A
So, I'll drag this a bit to the right so we can still see the video playing.
Speaker A
Let's make it tablet size. And now let's slowly start animating it. I'll first get access to the setter function called set texture, which is coming from use MacBook store, the global store of our application. That'll allow us to
Speaker A
change the video on scroll. Next, the first thing we want to do is preload all feature videos during component mount. That way, we can easily just modify them within the screen as we scroll down. So, I'll run use effect.
Speaker A
And as soon as the component loads meaning an empty dependency array, oh and don't forget to import the use effect from React. We want to go over the feature sequence coming from constants.
Speaker A
And for each one of these features we're going to get access to it and immediately preload it. We can do that by creating a virtual video element in memory which won't be added to the DOM.
Speaker A
I'll call it v and that'll be equal to document.createelement of a type video. This will allow us to start downloading the video data without displaying it. Then we want to configure the video element with optimal settings for smooth playback by saying object
Speaker A
datign v as in video. And then we can give it a couple of settings such as the source which is going to be feature dot video path muted because we don't need any sound. Plays inline to true inline
Speaker A
here will be capitalized because we're in JavaScript. preload will be set to auto, which means that it'll aggressively load all the data of the video so it's ready to be played when needed.
Speaker A
And then we'll also set the cross origin to anonymous which will allow us to load videos from different domains like a CDN. And then we can run v.load which will start fetching the video. Perfect.
Speaker A
So once we preload the videos, we want to enable the 3D model rotation on scroll. Remember this how it rotates?
Speaker A
Well, that's what we want to achieve. So, right below, I'll open up the use G sub hook and we'll start with a 3D model rotation animation and we'll also pass an empty dependency array to this UG sub hook.
Speaker A
Let's start by creating our first timeline specifically for the model. So we can call it model timeline and it'll be equal to gap which we have to import from the top dot timeline. So that's going to be import
Speaker A
gap from GSAP. And then we can pass some additional options to it which will be based on this scroll trigger plugin.
Speaker A
We'll make the trigger the canvas itself. So as soon as the F canvas, the feature canvas gets into the view or rather we'll start it when the top of that component gets to the top of the viewport and we'll end it when the
Speaker A
bottom of the component hits the top of the viewport. We'll set the scrub to one so that it is one to one matched with our scrolling and pin to true. This will pin the canvas in place during the
Speaker A
scroll creating that scroll distance. So if you now reload, you can see how there's so much more scroll for the same thing. Now once we have this timeline we also want to sync the feature content. So different types of videos
Speaker A
that are going to display different features. That'll be equal to timeline is equal to gap.timeline based on the scroll trigger as before.
Speaker A
And it'll more or less be the same thing as we had here. We'll simply have the scroll trigger.
Speaker A
The trigger will still be the canvas. We'll start from top center. We'll end at bottom top. And we just want to scrub it but not pin it. Perfect. Finally, we want to spin the 3D model by saying 3D
Speaker A
spin. If group ref.curren current exists. Then we have the model timeline that will animate to and first you have to choose which target you want to animate. So that is the group pref.curren.
Speaker A
rotrotation and to it we'll apply some settings such as the y position of math.py * 2. And what this does is it'll rotate it 360° or two radians 2 * math.pi pi around the y ais that means that it'll
Speaker A
make a full circle and we can do it with an easing. So ease of power one dot in out. You'll see me use this one often. Perfect. So now the 3D model should be spinning as well. Check this out.
Speaker A
It'll make a circle around the yaxis. And finally, we have to sync the content and the texture so that these blocks appear around the model and that the video that's being played on screen also changes. So let's do that. Next, I'll
Speaker A
say content and texture sync. I'll open up the timeline and run the call function which allows us to call an additional function, in this case set texture function. And we will set texture to forward slash videos slashfeature-1.mpp4.
Speaker A
And we will animate the box at the same time. Box one to opacity of one. So it's actually visible. Y of zero with a delay of one.
Speaker A
Now I'll go full screen so we can very soon see those boxes that appear. But for now, I will actually duplicate these two call and two functions a couple of times for all the different text boxes that we have. One 2 3 4. That way we
Speaker A
have the box one, we have box two box three and five because we're starting from the first one. For the features two, three four, and five. Five different features.
Speaker A
For all of them, we'll change the opacity to one, the Y position to zero and I'll remove the delays from all of them besides the first one because they'll be appearing one after another.
Speaker A
We can also change the box names to box two, three, four, and five. Now, if you save this, you'll see this one that says summarize and draft replies to emails instantly, so you stay on top of your inbox. Then as you scroll, the new video
Speaker A
will load up and it'll say generate images and edit them with ease. As you continue scrolling, you'll see another one that says turn long articles and reports or notes into clear byite-size summaries and so on. And you can notice
Speaker A
that sometimes we have that suspense showing up which shows us the loading animation. Even though we preloaded the videos, it still takes some time to update the textures. I know it's not pretty and the only way I was able to go
Speaker A
around it is when I used images. So if you want to just display an image of what that feature does, we would be able to completely eliminate the loading. But since we're using the images, the loading has to be there. But it's only
Speaker A
for this first scroll. If you continue scrolling up and down, it'll be gone. And now alongside just displaying the text, such as generate your images, we can also add some additional information. So besides just saying feature text, let's edit this JSX a bit
Speaker A
more. By rendering an image with a source of feature icon and an all tag of feature.
Speaker A
Below the image, we can render a p tag that'll start with a span element with a class name of text-wite and it'll render the feature. And finally below the span, we'll render the feature.ext. text. So now we have this
Speaker A
nice looking text that has the image. So we can very easily at a glance see what that feature is all about. And if you bring this to a tablet view, you'll notice that everything still looks great. The laptop is big and you can
Speaker A
still see the features. Sometimes it goes over them, which also, I believe adds that special touch to this 3D element. And that means that the feature section is done. So let's go ahead and commit it by running get add dot get
Speaker A
commit-m implement features section and then get push. Just two more sections to go and the next one on our list is the highlight section. So let's implement that one next.
Speaker A
Let's implement this next section which seems just like a vertical list of cards on mobile but if you expand it a bit you'll quickly see that it turns into a bento box layout. Yep. right here on desktop. There's never been a better
Speaker A
time to upgrade. So, let's go ahead and implement it. Right here under highlights, I'll turn this into a section with an ID equal to highlights.
Speaker A
Then, within it, I'll render an H2. And now, we want to copy the text over from a finished website. There's never been a better time to upgrade. Feel free to copy it or just type it yourself. And then below that, we'll have an H3 that
Speaker A
says, "Here's what you get with the new MacBook Pro." So now, if you head back over to your current application, and if you scroll down, you can see this title.
Speaker A
Below that, we want to display these four different cards. So, let's create a new div with a class name equal to masonry. This layout is also called a masonry layout. If you Google it, you'll see something that looks like this. You
Speaker A
could also consider it a Pinterest layout because I believe they were the first ones to popularize it. So within this layout, you can create another div with a class name equal to left column.
Speaker A
And then within the left column, you can create another div that'll have an image of source/laptop.png with an all tag of laptop. And below it you can have a P tag that'll say fly through demanding tasks up to 9.8 times
Speaker A
faster. And now on the current website you cannot see it yet. That's because this left column has an opacity of zero applied to it at the start. If we move it, you'll be able to see it here, but
Speaker A
we're keeping it zero at the start so that we can then animate it in later on.
Speaker A
Now, on the left side, there's this stunning liquid HDR display as well. So let's display it by duplicating this div just below and displaying a sun.png image with an al tag of sun. And for the p tag we'll
Speaker A
just say something like a stunning with a break tag liquid HDR with a break tag and then display. That'll replicate this. Now we can focus on the right side. I'll simply duplicate this left side below and rename it to right column
Speaker A
within which I'll have a class name equal to apple gradient. For this one, we really want to make it pop with this border. The image within it will have an AI.png path and the all tag will be AI and it can say something
Speaker A
like built for Apple Intelligence within the P tag. We'll add a bit of a break.
Speaker A
And then this part right here can be put into a span element. So we apply a gradient to it. And finally for the last section, we'll have a div within which there's going to be a battery.png image with the al tag of battery.
Speaker A
And it'll say up to 14 hours more battery life or up to 24 hours in total.
Speaker A
Specifically, we can put 14 hours more within a span element like this. And then that span will have a class name equal to green gradient. And then we can put a space before it and after it as well to divide it from the other textual
Speaker A
elements. And then for this one we can make it a bit grayish. So up to 24 hours will be within another span element.
Speaker A
This one will have a class name of text- dark 100. And we also want to apply a space right before it. Perfect. Now, we cannot see those sections just yet because they're hidden starting with the opacity of zero. So, if you head over to
Speaker A
the left and the right column and remove the opacity, you'll of course be able to see those sections. But, we want to animate them from not being visible at all to being fully visible. So, let's do just that right here above where first
Speaker A
we'll figure out whether we're on the phone or on desktop because depending on that we'll have different animations.
Speaker A
So is mobile use media query. You already know the deal where the query will be a max width of 1,024 pixels. And right below it, we'll open up the use gap hook which will run only once at the
Speaker A
start within which we want to use the gap library. So you have to import gap from GSA and then you'll say gap.2. We don't have to have from because we already set the opacity to zero. So we're just animating two specifically
Speaker A
targeting the left column and the right column. And we'll apply it based on the scroll trigger. The trigger of the scroll trigger will be the highlights section itself. And we'll start depending on whether we're on mobile or in desktop
Speaker A
from bottom bottom or from top center. I found those values to work the best when animating this section on desktop and mobile screens.
Speaker A
We'll also outside of the scroll trigger apply some additional properties such as Y to zero, opacity to one, stagger of a 0.5. So we stagger each card by 0.5 seconds and duration to one as well as the ease of power one dot in out. So now
Speaker A
if you reload the page and scroll down you'll notice that they slowly appear. If you want to prolong the animation maybe we can make this top top. That way it'll start as soon as the top of the component reaches the top of the
Speaker A
viewport. So if you scroll fast, you'll be able to see these cards animating. And in mobile, it looks great as well.
Speaker A
Before we head over to the last component of this application, yep we're there already. And that is the footer. Let's not forget to commit the changes right here by running get add dot get commit-m implement highlights section and get
Speaker A
push. With that in mind, we can get started with the footer. Our footer will be super simple as is the footer of all professional websites out there. Sure, you can make the websites pop with 3D models, videos, and
Speaker A
make people really want to buy your products, but top navigation and bottom nav is for giving them the links to actually buy it. So, every single important link that has to be there, but we didn't include it throughout the
Speaker A
landing page itself. Well, now we can show it here. So, let's replicate this very simple footer on our own. Of course, it looks good on tablet and desktop devices as well. So, as code rabbit earlier suggested, let's use an
Speaker A
HTML 5 semantic footer tag and within it have a div with a class name of info.
Speaker A
This is for this line right here and the logo. So, I'll immediately create a new P tag and duplicate this piece of text within it. Of course, you can actually turn this into clickable links or anchor tags. And then below it, I'll render an
Speaker A
image. This one will be a source of forward slash logo. SVG with an al tag of apple logo. And then we want to go below the div create one horizontal rule which is this line right here. Below it
Speaker A
display another div with a class name of links and then render a p tag within which we will display the copyright.
Speaker A
And then below that p tag we'll render a ul an unordered list through which we will map over the footer links.
Speaker A
And then for each one of these links we will automatically return an li with a key equal to link label or we can just dstructure the label and the link from the link itself. So now we just have to
Speaker A
say label within it. We will also render an anchor tag that'll have an href of link and it'll display the label for each one of these links.
Speaker A
If you've done that, your footer should now look like this. And we can also check it out on desktop. If you hover over those links, they'll appear clickable. For now, they don't really lead anywhere, but rather bring us back
Speaker A
to the hero section. So now that we're officially done with all of these sections of our application, just before we commit it and open up a final PR let's run it through the Code Rabbit CLI one more time and press enter to review
Speaker A
all the latest changes. Let's channel the spirit of senior developers. Look at that. Not too many issues, just in the performance.jsx file where we need to improve the accessibility with some meaningful alt text. Yep. Performance image one, two, three, and so on. Which
Speaker A
you can either auto apply or let's not be too lazy. Let's head over into the performance file and immediately just apply a nice al tag right here by saying performance image number. And then we can render the actual index of that plus
Speaker A
one. So it's going to be performance 1 2 3 and so on. And we can turn this into a template string. One is fixed. And finally, we have to remove the duplicate wrapper element. Oh, is that really the
Speaker A
case? It appears it is. Okay, this was actually a super nice catch. Also, in performance on line 7172 we have a duplicate wrapper. We definitely want to remove this one right here. And with that in mind, we have
Speaker A
resolved all of those issues and we're ready to push the changes over to GitHub by running git add dot git commit-m implement footer and git push. That'll allow us to open up a new PR and know for a fact whether we are indeed ready
Speaker A
to merge it into the main branch. Before we merge it, code rabbit has to take a look and let me know that it's okay with merging all of those changes. So, I'll go grab a cup of coffee, see what
Speaker A
feedback it comes with, and let's fix it so we can be 100% certain that we're finalizing this app with a clean and error-free codebase. And here we are with a full walkthrough of the added features. I mean, just for a second
Speaker A
imagine that you didn't write this code on your own, but rather that a colleague or another developer in your team wrote the code. Seeing this is super useful because it explains exactly what happened. This PR will add four UI
Speaker A
sections performance features highlights, and the footer into the app composition. We're implementing the GUB driven scroll animations and React 3 fiber 3D model interactions, including the store managed video texture and color application to a magot model. This is exactly what we've done. And we get
Speaker A
an even more detailed diagram. This one explains how the model spins as we scroll the page. And here we can see a bit more about the performance implications. This time the comments include minor issues such as adding a
Speaker A
key prop to a mapped feature. Oh, look at that. I really did forget to add a key to a mapped element which can often cause some inconsistencies. So, a very simple thing is to just add it right here. Last time I showed you how we can
Speaker A
immediately commit this suggestion so that it now includes a key. But as you can see, you'll have to carefully review this before committing because sometimes, like in this case, it's adding more stuff than it's taking away.
Speaker A
It's starting to add an additional div but it's not removing one at the top.
Speaker A
And then it's ending without a div, but here it has a new one. So instead of doing this manually, the other thing that it gives you is the prompt for an AI agent. Nowadays, almost every single IDE has AI features built in. So just
Speaker A
copy the prompt, go back within your application. In this case, I'll open up Juni and I'll simply paste what I copied over from Code Rabbit. And in a second a key attribute was added to the mapped element without us having to touch a
Speaker A
piece of code. Perfect. And besides the nitpicky comments, that's it for this review. So before I merge, let me quickly commit the change by running git add dot get commit-m add a key when mapping over the elements and get push.
Speaker A
With those changes implemented, we are now ready to merge this PR. And the last thing we have to do is deploy this app to the internet so that everyone can open it up. We can do that right here within the terminal by
Speaker A
running mpm run build. This will create the optimized production build of the application. Once you have it, head back over to your hosting dashboard and open up the dashboard for this specific application. Then open up the file manager. Head over into public html.
Speaker A
delete the default PHP and instead you can rightclick on the disc folder that was created for you when we ran MPM run build and you can open it up within Finder or File Explorer if you're in Windows. Once you have it, you can head
Speaker A
over into it and then drag and drop all the files to this public html. It'll take some time because we have some larger files over there, but as soon as it is done, you can head back over to
Speaker A
your dashboard and just click on your domain name. Immediately your application will open up but this time not on localhost but rather on a real online domain name that is HTTPS secured. As you open it up we first have
Speaker A
this MacBook opening up section. Then we have this 3D model that you can move around and change its colors and sizes.
Speaker A
Moving down, we have this beautiful full page video section that then transitions over into a mask element showing the M4 chip that is powering the MacBook that you're about to buy. Then we can see what that MacBook is capable of by
Speaker A
displaying all of these different apps. And finally, we can see it in a new light by seeing the exact types of apps that it can run, most of them being AI powered. And of course, there's never been a better time to upgrade, which is
Speaker A
our last section. And finally, the footer. This was such an amazing build and I'm super happy that you came all the way to the end of this course because now you've not only built, but also deployed this amazing GAP and 3JS
Speaker A
powered application. If you like this course, head over to jsmastery.com and take a look at the web animations full course where you'll learn how to come up with animations on your own. We'll start from the beginning by getting started
Speaker A
targeting the elements and understanding properties all the way to different easings which shows you how the animation actually plays out in real time using animated graphics and you can see that on the elements moving around.
Speaker A
Not only that, at the end you will build and deploy a GTA 6 animated website with all the sections that the real website has. And finally, we'll dive into the emotional design case studies where you can see how Apple, Dualingo, Stripe
Speaker A
Revolute, and other huge companies are using animations to their advantage. Once again, congrats on coming to the end of this course, and I'll see you on the inside.
Topics:ReactJSThree.jsGSAPTailwind CSSFrontend Development3D AnimationScroll TriggerWeb AnimationHostingCode Reviews











