Project.js
Is this file modifiable? — YES
Section titled “Is this file modifiable? — YES”Project.js is the entry point of our site: this means we indicate to our Vite configuration that we want to use it as our input and it will be the point where the build will start:
export default defineConfig({ //...other configuration build: { //...other configuration
rollupOptions: { input: { Project: resolve(__dirname + "/src/js/Project.js"), Appbackend: resolve(__dirname + "/src/js/vite_additional_input/Appbackend.js"), ProjectStyles: resolve(__dirname + "/src/js/ProjectStyles.js"), },
//...continue build instructions }, },});So this file will be executed once, the first time our site loads. Let’s dissect the file and see what every part does!
This file is a Terra class, and as such, it follows our Terra class patterns.
constructor()
Section titled “constructor()”constructor() { window.isFired = true; // prevent multiple instances of the project class this.DOM = { images: document.querySelector("img"), lotties: document.querySelectorAll(".js--lottie-element"),
heroA: document.querySelector(".c--hero-a"), heroB: document.querySelector(".c--hero-b"), move: document.querySelectorAll(".js--moveItem"), };
// terra debug mode, add ?debug to the url to enable debug mode this.terraDebug = getQueryParam("debug");
this.boostify = new Boostify({ debug: false, license: import.meta.env.VITE_LICENSE_KEY, });
this.Manager = new Manager({ terraDebug: this.terraDebug }); this.Manager.allocateInstances([ "MoveItem", "Collapsify", "InfiniteMarquee", "Slider" ]); // Reserves the space for future instances this.Manager.modifyHeight(['Collapsify','InfiniteMarquee'])
this.init(); }The constructor in our Project.js needs to perform several actions:
- checks if the Project class was already loaded and prevents double loading
- captures every element that needs to be preloaded or animated from the DOM
- instances our Boostify library
- instances our Manager
- allocates space for our libraries in the Manager
- defines which libraries from the ones imported modify the height of the page (necessary to perform actions such as anchor to)
Know more about the actions you will need to perform in this file in the Introducing new libraries section of the sidebar.
init()
Section titled “init()”async init() { try { this.boostify = new Boostify({ debug: false, license: import.meta.env.VITE_LICENSE_KEY, });
await assetManager({ where: "preload", types: ["preloadImages", "preloadLotties", "preloadVideos"], debug: this.terraDebug, libraryManager: this.Manager, loadLibraries: async ({currentDOM}) => { return await loadExtraAssets({ debug: this.terraDebug, manager: this.Manager, boostify: this.boostify, currentDOM }); }, progress: (percent) => { document.getElementById("progress-counter").innerHTML = Math.round(percent) + "%"; if (percent >= 100) { gsap.to(".c--progress-a", { scale: 0, duration: 0.3, ease: "power2.inOut", }); } }, });
await new Promise((resolve) => setTimeout(resolve, 200)); } catch (e) { console.log(e); }Our init method:
- initializes our Boostify
- calls our asset manager to introduce our preloaders and other needed libraries
- documents the progress through all of these actions to display a real progress bar
The asset manager is one of the most important parts of our application, dive deep in our manager section!
finally { var tl = gsap.timeline({ defaults: { duration: 0.8, ease: "power1.inOut" }, onUpdate: async () => { // //* Check if the animation is at least 50% complete and the function hasn't been executed yet if (tl.progress() >= 0.5 && !this.halfwayExecuted) { this.halfwayExecuted = true; const { default: Main } = await import("@js/Main.js"); new Main({ boostify: this.boostify, terraDebug: this.terraDebug, Manager: this.Manager, }); } if (this.terraDebug) { (async () => { try { const { terraDebugger } = await import( "@terrahq/helpers/terraDebugger" ); terraDebugger({ submitQA: "clickup-url" }); } catch (error) { console.error("Error loading the debugger module:", error); } })(); } }, }); tl.to(".c--preloader-a", { duration: 0.5, opacity: 0, delay: 0.3, ease: "power2.inOut", pointerEvents: "none", }).add("preloaderFinished");
if (this.DOM.heroA) { tl.add(new (this.Manager.getLibrary("heroA"))()); } if (this.DOM.heroB) { tl.add(new (this.Manager.getLibrary("heroB"))()); } if (this.DOM.move) { this.DOM.move.forEach((element, index) => { const MoveItem = this.Manager.getLibrary("moveItem"); if (isElementInViewport({ el: element, debug: this.terraDebug })) { tl.add(new MoveItem({ play: "instant", element: element })); } else { this.Manager.addInstance( "MoveItem", new MoveItem({ play: "onscroll", element: element }) ); } }); } }The finally block of our init() method is in charge of instancing our Main file:
- creates a GSAP timeline
- when the timeline has passed its 50 % mark, it imports and instances
Main - once that is done, the preloader is eased out of view
- if we have animations that do not depend on classes controlled by handlers, it adds them to the GSAP timeline, depending on if they are in the viewport or not
Creating instances immediately only of the things on the viewport is one of the most important ways that we at Terra improve the performance of our sites. We do not need to load every library and every instance when we enter the page. If you don’t see it, we don’t load it (at first).
This file is our entry point, it is in charge of instancing our Manager, allocating space for our libraries, importing all of our resources if needed, instancing our Main file and creating an animation timeline with all of our animated elements in it.