PROJECT 4: Final Project

Objective: Build... anything!

Tools: ScrollMagic, barba.js, GSAP, highlight.js

Beginning of the End

For the final project of SEB71, we were given free reign to make anything we wanted. I had a lot of trouble trying to come up with a concrete idea. I thought of making a website based on coding minigames. A website for teaching coding. Even a full blown game. In the end, I thought to do something to celebrate my achievements in this course because, whether I think so or not, I have done quite a lot in the 12 weeks of SEB71.

This website is more or less a portfolio. I'm hesistant to call it one because of the content I've put in this but regardless, it serves as a documentation of all the projects I've worked on. This will definitely be something that I will be updating very frequently so maybe someday, I'll be more sure of what it is. For now, I just hope it's not too boring.

It wasn't enough to just reflect on my projects though. I also wanted to try and force myself to use more artsy things so I tried out a bunch of new libraries. I'll be demystifying my site by going through them so if you don't want to ruin the magic, look away.

Coding the Beast

The first library on the list is one that's present in every page: MagicScroll. Even now, you'll probably notice it as it is responsible for both the parallax effects and the front page zoom animation.


                    .parallaxParent {
                        height: 100vh;
                        overflow: hidden;
                    }
                    .parallaxParent > * {
                        height: 100%;
                        position: relative;
                        top: -40%;
                        background-size: cover;
                        background-repeat: no-repeat;
                    }
                        

                    let parallaxController = new ScrollMagic.Controller({globalSceneOptions: {triggerHook: "onEnter", duration: "200%"}});
    
                    // build scenes
                    let parallaxSections = document.querySelectorAll(".parallaxParent")
                    let parallaxSectionsDiv = document.querySelectorAll(".parallaxParent > div")
    
                    for (let i = 0; i < parallaxSections.length; i++ ) {
                    new ScrollMagic.Scene({
                            triggerElement: parallaxSections[i]
                        })
                        .setTween(parallaxSectionsDiv[i], {y: "80%", ease: Linear.easeNone})
                        .addTo(parallaxController);
                    }
                    

ScrollMagic works using Controllers and Scenes. Scenes are all the individual elements to be animated and are added to a Controller which defines the rules under which they animate. In the case of the parallax scrolling, it triggers whenever a parallaxParent div is entered which then causes its child, the actual background image, to slowly move vertically by 80% of its height, creating the slow background scroll effect.


                    let headController = new ScrollMagic.Controller()

                    let headScene = new ScrollMagic.Scene({
                                        triggerElement: ".filler",
                                        triggerHook: 0.9,
                                    })
                                    .setTween([".head-image", ".main-nav", ".head-message"], 1, {scale: 10, display: 'none'})
                                    .addTo(headController)
                    
                    let projectsController = new ScrollMagic.Controller()
                    
                    let whiteScene = new ScrollMagic.Scene({
                                        triggerElement: ".trigger",
                                        triggerHook: 0
                                    })
                                    .setTween(".home-div", 1, {
                                        backgroundImage: "radial-gradient(white 15%, black 80%)"
                                    })
                                    .addTo(projectsController)
                    

                        let headController = new ScrollMagic.Controller()
    
                        let headScene = new ScrollMagic.Scene({
                                            triggerElement: ".filler",
                                            triggerHook: 0.9,
                                        })
                                        .setTween([".head-image", ".main-nav", ".head-message"], 1, {scale: 10, display: 'none'})
                                        .addTo(headController)
                        
                        let projectsController = new ScrollMagic.Controller()
                        
                        let whiteScene = new ScrollMagic.Scene({
                                            triggerElement: ".trigger",
                                            triggerHook: 0
                                        })
                                        .setTween(".home-div", 1, {
                                            backgroundImage: "radial-gradient(white 15%, black 80%)"
                                        })
                                        .addTo(projectsController)
                        

For a slightly easier example, look at the code for the home page. When you scroll down, you actually touch divs that serve as triggers for the animations. One scroll from the top puts the top of the screen in contact with a div that then triggers the enlarging of all the home elements to simulate a zoom. As soon as you scroll to the bottom, another div is reached which then triggers the projects to appear.

ScrollMagic also comes with debug indicators to see where your triggers are. See the gif in the parallax scrolling below.

Next up is barba.js. This was probably the trickiest library to work with for this project. It's responsible for the transitions you see whenever you switch pages, avoiding the ugly page refreshes that would normally happen. As a result, barba.js is able to mimic the fluidity of a single page application despite my website having multiple pages. The way it works also mirrors a real SPA. In my HTML, I needed to mark out what elements would stay consistent between any page so switching pages would only render what's specific to that page.


                <body data-barba="wrapper">
                    <!-- put here content that will not change
                    between your pages, like <header> or <nav> -->
                  
                    <main data-barba="container" data-barba-namespace="home">
                      <!-- put here the content you wish to change
                      between your pages, like your main content <h1> or <p> -->
                    </main>
                  
                    <!-- put here content that will not change
                    between your pages, like <footer> -->
                </body>
                

The problem with all this however, is that JS scripts and CSS files were only set once and shared between pages. This meant I couldn't have different files for each page. Everything had to be in a single common script and stylesheet. I also needed a function in said script to reload all my other functions or the website would just break. On top of all this, I still needed extra logic for barba to run it all after a transition:


                barba.hooks.after(() => {
                    const bottomDOM = document.getElementsByTagName("body")[0]
                    const newScript = document.createElement("script")
                    const oldScript = document.querySelector(".main-script")
                    newScript.src = "./everything.js"
                    newScript.className = "main-script"
                    oldScript.remove()
                    bottomDOM.appendChild(newScript)
                })
            

barba.hooks.after basically executed this code 'after' everything else had already been loaded, removing and reattaching the script to the DOM itself to re-run it. There's also a bit more to the barba specific JavaScript to make the transition actually play:


                function PageTransition() {
                    let tl = gsap.timeline()
                
                    tl.to(".transition", {
                        duration: 1,
                        opacity: 1,
                    })
                }
                
                function delay(n) {
                    n = n || 0;
                    return new Promise((done) => {
                        setTimeout(() => {
                            done()
                        }, n)
                    })
                }
                
                barba.init({
                    sync: false,
                    transitions: [
                        {
                            async leave(data) {
                                window.scrollTo(0, 0);
                                const done = this.async()
                
                                PageTransition()
                                await delay(2000)
                                done()
                            },
                            enter(data) {
                                gsap.fromTo(".transition", {opacity: 1}, {opacity: 0, duration: 2})
                            }
                        }
                    ]
                })
                

barba.init is what initializes the transitions. You can see there is one for leaving a page and another for entering. Behind the scenes, these transititions simply toggle the opacity of a div that covers the viewport, using GSAP, an animation library, to animate the change in state. As a matter of fact, ScrollMagic also uses GSAP for its animations and the syntax is practically the same.

Lastly for the new libraries is highlight.js. This is responsible for all the cool looking code boxes. It works by styling the pre and code tags and then running a function to highlight the colors based on the language used:


                    <pre class="theme-atom-one-dark"><code class="hljs-code language-javascript"></code>
                    // insert JavaScript here
                    </code></pre>
                
highlight.js inception

Unsurprisingly, this was the least painful to get working and I'm glad it did because it influenced my design ideas quite a bit. Without it, I would have had to display all my code with images which wouldn't have looked half as nice, to the point where I might have decided not to bother with them at all.

Closing Thoughts

The feeling I had with highlight.js was definitely a recurring theme for all the libraries I learned for this website; as I figured out what I could do with them, they shaped what I was going to build. For an extreme example, I couldn't figure out a layout for my project pages until I was directed to the parallax scrolling function from ScrollMagic. Then I decided to use the parallax sections as transition images between blocks and built the rest of the project page from there.

Speaking of design, I must have changed my ideas at least 3-4 times while working on this project. At first, I was going to have a 'storytelling' theme, with each page being a literal page from a book. Similarly, I thought of a 'lab report' theme where I would document my projects in third person from the view of a scientist. In the end, I settled on having a 'mindscape' theme. Still, even that got warped as I continued to build this site. Eventually, I figured that whatever I came up with would be the purest representation of my mind anyway so I stopped thinking too hard about the theme and just did things that I liked. Perhaps now my website is a bit too informal but I can always change that later now that I, well, have a website to touch up.

All in all, this project has been a real eye opener when it comes to design. I knew I wasn't going to become a professional web designer over a single week but I do feel more inspired to try messing with CSS and just paying it more mind in general. In the future, I am absolutely going to return to this site to make it better, or just use it as a CSS playground of sorts.

And that's it for my SEB71 journey. Feel free to go back to my other projects: Project 1: Tic-Tac-Toe, Project 2: GameShare™, or Project 3: Petrol Down Under