Portfolio Vinícius Roveri (Go To The Site)
Project where I showcase all my abilities in Web Development, UX design, Motion Graphics and 3D Modelling, all merged together in one single piece. Made using various professional and industry standard tools for the front-end development such as the NextJS framework, GSAP for JavaScript animations, React Three Fiber which is an extension of the ThreeJS library, Bodymovin & Lottie-Web for making After Effects animations web-ready, and a whole more that when combined with the standard NextJS, React, Typescript & Tailwind, create the art you see on the screen.
The idea of bringing together all my skills in different areas of creativity and art is harder than it looks. The tools are not the main problem, I know I can learn how to use any library to make the idea in my mind come to life, so the hard part is exactly how do I get those ideas in my mind?
As creativity is basically using your experience on top of something that exists to create something new, I went searching for inspiration in other professional web developers' portfolios and used that to make something I thought would make sense to both showcase my personality and my technical skills.
The two main inspirations were iuri.is for the clean design and awesome display of information allied with the use of animations, and mainly itsSharlee for the insane use of 3D, animation and design to reinforce his also awesome branding work, to create what is probably my favorite portfolio ever.
With that references I went to work and united all my knowledge with a goal of displaying my work in a clean, interactive and concise way, to best show anyone who comes to my portfolio not only what Vinícius does, but also who Vinícius is.
After learning the NextJS
framework the need of it in the project for me was made very clear, so after the Portfolio was already finished with React I updated it just by creating a Next app, copying the files of the original project into it and making some slight necessary adjustments. The main reason I did that was the built-in Static Site Generation
by NextJS
, which helped with the loading inconsistency the starting animations were facing across multiple devices, now with the static site I can almost guarantee each animation will play at the right time, as all the assets are already previously created on the build
process and the client browser will not have to do so when opening the site.
A great part of my animation degree was in fact about 3D animation, so I already had the skills necessary to pull off the models I nedded without trouble. For that I used Blender, my favorite 3D software, and after all the modelling of the <VR>
, the orange globe, the path and each icon, it was time to put it on the website.
-
I used
React-Three-Fiber
andReact-Three-Drei
for that task, these are React extensions of the popular libraryThreeJS
, the industry standard for joining 3D modelling and web development. Back in Blender I exported each model as GLTF and put them in thepublic
folder of the project, after that I used theGLTFJSX
library to automatically createJSX Functional Components
from each model. -
The first animation for them was in the intro, for that I used the
useEffect
hook to execute the code only on the component mounting, and GSAP to animate the position of each mesh as I wanted, using theuseRef
to reference to GSAP the component I wanted to animate. The second and last part was to give the<VR>
model and each icon on the globe a "Keep Alive", a technique in animation to give more life to a prop/character. For that I used theFloat
component fromReact-Three-Drei
that is the easiest and most perfomatic option for this task.
Lastly I had to decide when each animation would play. To do so I created twoconsts
on theglobalConstants
file, one referring to the duration of each animation, and the other to the delay each animation have. This way I can just import eachconst
wherever I need them to configure the animations, and most importantly of all, easily have a centralized control over all timings. -
The main point of the globe is that it's interactive and the user must spin it to go to a certain part of the portfolio, I'll get into the performance bits later in this document, but the best solution for that was a combo between both the
React-Three
versions used,fiber
anddrei
. To make the models react to scroll I usedDrei
'sScrollControls
component, after that I had two things to figure out: how to make the globe spin with the scroll and how to make it stay fixed at the bottom of the page. For both these tasks I went with the hooksuseScroll
anduseFrame
, which I could capture the current scroll offset and automatically animate the mesh accordingly with, respectively.
For both of these problems I had to develop an algorithm that calculates what the position/rotation of the globe should be at any given scroll offset, based on its starting and ending values, and the total size of the scroll. After that I simply put this algorithm into theuseFrame
so it's always being executed and the functionalities were working perfectly.
As the meshes' animations were already explained earlier in the Meshes' animations section, let's talk about the textbox animations with GSAP. First of all it's important to know that the textbox animations are triggered with an useEffect
with the dependency of the global state activeSection
created as an atom
from the Recoil
library, and updated from the SphereAndPath
component everytime the scroll values enters a range of certain content area (these value ranges are all predetermined in the globalConstants
file). Other important fact is that the textbox content and background are two different sections
, because this way both the styling and animations were made easier. That being said, the two main animations are the closing/opening when the content of the box changes, and the appearing/disappearing when the user scrolls into or away from the globe respectively.
The first one is made animating out the text, making the width of the background 0%, and then reversing those stages so the background grows to its size again, and then the text is animated in.
The second one is an easy animation but with one detail that makes it harder: The animation is the same as the first one, but with the movement of up or down, depending if it's appearing or disappearing. To determine which animation should play I check either the new current area is within the range of the globe, and if it isn't I add the movement down at the end of the animation to hide it. But if it is within that range I don't have to worry at all, as in the standard animation the box position is configured to go to its default, it'll be animated correctly automatically.
This one have a special place in my heart, as before getting into Web Development my main passion was Motion Graphics, and what really got me into deeply learning programming was when I once discovered my Motion Graphics animations not only could be made with code inside After Effects, but also could be transformed in JSON
files to be used in actual programming projects, using Bodymovin
, the tool I actually used in this part of the project.
-
Back in After Effects I had to have an idea of what exactly to do in this intro animation. The first point is that it had to contain my brand, so I started looking at what I could do with the
<VR>
and realized that as it's supposed to resemble a code, it was a good idea to go with a typing animation on it. After that I added the animation to reveal my full name and thought about a simple way to make it disappear. This last step was added so in case the globe appearing animation was out of sync due to performance issues, my name would get out of the screen a nice way (I'll get more into this topics as we go). After the animation was done I used theBodymovin
extension to export it as aJSON
file. -
To use the
JSON
animation data on the website I used theLottie-Web
library with the standard configuration given in their documentation. After the animation is complete itssvg
is destroyed using the.destroy()
method from theLottie-Web
, and I give its containersection
tag adisplay: none
so it get out of the way of the incoming canvas with the 3D meshes.
As the project uses very heavy assets like all 3D models and animations, the performance issues were being a huge deal before all optimizations. Each one demanded its own solution and I'm gonna explain them here:
-
By far the best improvement I could ever make was to reduce the file size of the 3D models, as they make up for almost the whole site. I did that in Blender by removing unnecessary edges (not worrying about topology as it's not necessary in this context) and exporting the models as low-poly as possible. This change alone reduced the sum of the size of the 3d files from 63mb to less than 4mb, a great 94% decrease.
-
This was a deal I had to break into to parts: Making the animation less heavy, and giving time for the browser to load it on start. The first part was back in After Effects, I did a lot of obvious optimizations (that for some reason I haven't done before) like removing some elements, hiding layers when they had no more content, using simpler shapes and avoid using unnecessary masks and scripts. And the second part I solved by manually creating a loading screen when the site is initialized, and giving it a predetermined delay before creating the svg of the animation. This delay is set manually, the other option was actually waiting for the animation to load before playing somehow, but as it's not that heavy of a process I decided to go with the first and easier solution.
-
If the canvas with the meshes was created right at the time when their animations should be played, the lack of loading time would create several inconsistencies among different hardware, not only that, as all animations are synchronized this would make the timing of every one of them be essentially random. To solve that I make the canvas appear some seconds before the animation of the meshes start, like that most browsers will have the necessary time to load the assets and execute everything as it was intended to.
-
This was happening because I was testing ways of animating its position/rotation values with
useFrame
according to the algorithm mentioned on the Interactivity with the models section, and when I tried to animate these values with theuseState
hook, it all became borderline unusable. So after looking up on theReact-Three
documentation I found out that the problem was thatuseState
works asynchronously, and asuseFrame
executes all the time, it was like I was making infinite requests for the position to change, and of course this takes much more effort than just changing the position right away. So the solution, as said in the documentation, was to animate the actual position and rotation props of the mesh, usinguseRef
and GSAP. This turned out to be the biggest performance problem of all, so once I solved it, it really was the last big effort before the website got pretty much ready to go to production. -
After all that optimizations I know that some of the remaining performance issues come from the
Float
animations, but I configured it as much as I could without making the animation get boring or stop. As the next step would probably be to remove it completely, I decided to keep it like that because as an artist I see that in this case it's a fair trade-off a bit less performance for me being able to complete the piece just as I want.
The decision of styling with Tailwind comes from my curiosity after discovering and learning this tool, to see to what extent it could help in a big project, if it was faster to style with it and if it was really worth it using it instead of the SCSS
.
-
The first impact was that I had to write so much less, so I instantly loved it, and seeing how the time and effort I saved was scaling throughout the project it really convinced me that this was the right tool for the task, and it got its spot as what I plan on using on my upcoming projects.
-
The main downside to Tailwind is that if you use it the normal way you get almost unreadable
class
names, and that lack of clean code was really bothering my inner Uncle Bob. So to fix that I extract the classes into aconst
, which is astring
, at the top of the file, and write everything that I want for eachclass
there. This way not only the reading and writing gets easier, but I also exercise the actually very helpfulCSS-in-JS
concept, all in a very easy and elegant fashion.