How to Create an Animated Three.JS Landing Page with Counter

Three.JS is a widely used JavaScript library for generating and creating impressive 3D graphics in browser using WebGL. In the following tutorial I will show you how to create a landing page with a counter. Covering creating geometry and adding effects, as well as adding a date counter that counts down to a deadline.

For the purposes of this demo we will be adding Three.JS to a static HTML page to keep it as simple as possible.

Step 1: Create the HTML and CSS

I am going to assume if you are reading this you have a decent understanding of HTML and CSS. First we want to just add the following CSS file to a bog standard HTML page.

body {
    font-family: 'Montserrat', sans-serif;
}

#overlay {
    background-color:black;
    opacity: 1;
    width:100%;
    height:100%;
}

.timerSec {
    position:'absolute';
    color:'white';
    top:'0';
    width :'20%';
    float:left;
    font-size:2em;
    margin-left:11%; 
}

.responsive {
    width: 80%;
    height: auto;
}

/* font */
@import url('https://fonts.googleapis.com/css?family=Montserrat');

/* Extra small devices (phones, 600px and down) */
@media only screen and (max-width: 600px) {
    .responsive{
        width: 70%;
        height: auto;
    }
} 

/* Small devices (portrait tablets and large phones, 600px and up) */
@media only screen and (min-width: 600px) {
    .responsive {
        width: 100%;
        height: auto;
    }
} 

/* Medium devices (landscape tablets, 768px and up) */
@media only screen and (min-width: 768px) {
    .responsive {
        width: 90%;
        height: auto;
    }
} 

Next we want to write the first part of our HTML, before proceeding into the real guts of this project – namely the Three.JS implementation.

The following code sets up our CSS and JS dependencies, make sure to download these following the structure in the GitHub link.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Animated Three JS Landing page demo 1</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body {
            font-family: 'Montserrat', sans-serif;
	    background-color: #f0f0f0;
	    margin: 0px;
            overflow: hidden;
        }

        label, input {
	    cursor: pointer;
	}
    </style>
<!-- link to our custum css file  -->
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body id="overlay">
    <!-- link to three js lib -->
    <script src="build/three.js"></script>
    <!--glitch lib loaders-->
    <script src="js/CopyShader.js"></script>
    <script src="js/DigitalGlitch.js"></script>
    <script src="js/EffectComposer.js"></script>
    <script src="js/RenderPass.js"></script>
    <script src="js/MaskPass.js"></script>
    <script src="js/ShaderPass.js"></script>
    <script src="js/GlitchPass.js"></script>
Step 2: Timer

OK, so now we have our dependencies in place we’re going to want to create with Three.JS.
This is a lot of code so take some time to read it to understand what is going on.

<script>
    var container;
    var camera, scene, raycaster, renderer;

    var mouse = new THREE.Vector2(), INTERSECTED;
    var radius = 100, theta = 0;
			
    var glitchPass;

    init();
    startTime();
    animate();
			
    function updateOptions() {
        var wildGlitch = document.getElementById('wildGlitch');
    }

    function startTime() {
        var x = setInterval(function() {
	    var countDownDate = new Date("Dec 25, 2019 00:00:00").getTime();
        // Get todays date and time
	var now = new Date().getTime();

	// Find the distance between now and the count down date
	var distance = countDownDate - now;

	// Time calculations for days, hours, minutes and seconds
	var days = Math.floor(distance / (1000 * 60 * 60 * 24));
	var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
	var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
	var seconds = Math.floor((distance % (1000 * 60)) / 1000);
        // var milliseconds = Math.floor((distance % (1000 * 60)) * 1000 /1000);
				
	// Display the result in the element with id="dem"o"
	document.getElementById("timer").innerHTML = "<h1>Three.JS Landing Page Demo 1</h1><h2>Countdown to Launch</h2><div class ='timerSec'>"+days + " Days " +'</div>'+'<div class ="timerSec">' +hours + " Hours "+
            '</div>'+'<div class ="timerSec">'+ minutes + " Minutes" +"</div><div class ='timerSec'>"+ seconds + " Seconds"+'</div></div>';

        // If the count down is finished, write some text 
	if (distance < 0) {
	    clearInterval(x);
	    document.getElementById("timer").innerHTML = "<h1>COUNTDOWN EXPIRED</h1>";
	}
    }, 1000);
}

The code above now creates our scene and a timer function with our deadline and some maths to workout the time to deadline, and appends it to the element with ID ‘timer’ in the HTML.

Next we want to create those elements on the canvas which the following snippet covers.

function init() {
    container = document.createElement('div');
    document.body.appendChild(container);

    var info = document.createElement('div');
    var timer = document.createElement('div');
    var timerSec = document.createElement('div');
    var overlay = document.createElement('div');
    var byem = document.createElement('div');
    var logo = document.createElement('div');
      
    timer.style.position = 'absolute';
    timer.style.color = 'white';
    timer.style.top = '0%';
    timer.style.textAlign = 'center';
    timer.style.width = '100%';
    timer.style.margin = '0 auto';
    timer.innerHTML = '<div id = "timer"></div>'

    timerSec.style.cssFloat = "left";
    timerSec.style.width = "25%";
    timerSec.style.fontSize = "2em";
    timerSec.style.textAlign = 'center';
				
    byem.style.position = 'absolute';
    byem.style.color = 'white';
    byem.style.top = '80%';
    byem.style.fontSize = "1.4em";
    byem.style.textAlign = 'center';
    byem.style.width = '100%';
    byem.style.margin = '0 auto';
              
    byem.innerHTML = '<div id = "byem"><h1>Launching on Dec 25th </div>'

    // container.appendChild(info);
    container.appendChild(timer);
    timer.appendChild(timerSec);
    container.appendChild(byem);
    container.appendChild(logo);
Step 3: Three.JS Time!

Next up is the big one, using Three.JS to create some cubes and get all that groovy WebGL stuff going. Look at the code below which creates a scene, and adds 2000 cube elements to the canvas via a for loop.

camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);

    scene = new THREE.Scene();
    scene.background = new THREE.Color( 0x000000 );
			
    var light = new THREE.DirectionalLight( 0xffffff, 1 );
    light.position.set( 1, 1, 1 ).normalize();
    scene.add( light );

    var geometry = new THREE.BoxBufferGeometry( 10, 10, 10);

    for ( var i = 0; i < 2000; i++ ) {
        var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color:0xB4CDCD } ) );

        object.position.x = Math.random() * 800 - 400;
	object.position.y = Math.random() * 800 - 400;
	object.position.z = Math.random() * 800 - 400;

	object.rotation.x = Math.random() * 2 * Math.PI;
	object.rotation.y = Math.random() * 2 * Math.PI;
	object.rotation.z = Math.random() * 2 * Math.PI;

	object.scale.x = Math.random() + 0.5;
	object.scale.y = Math.random() + 0.5;
	object.scale.z = Math.random() + 0.5;

	scene.add( object );
    }

So what essentially is going on here? It can seem very arcane and complicated at first but read from top to bottom and it should soon become clear.

We are first creating a new three scene, we add some lighting via light, set its position and then proceed to create a for loop to add the objects with some math to randomise their position and scale. The for loop adds the objects 2000 times in this example but you can change it to any number you want! So experiment, go crazy! The way I have learnt the most with Three.JS was experimenting!

Ok almost there, finally copy this code to create the glitch effects as well as adding event listeners for mouse movement.

    raycaster = new THREE.Raycaster();

    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize(window.innerWidth, window.innerHeight);
    container.appendChild(renderer.domElement);
    document.addEventListener('mousemove', onDocumentMouseMove, false);

    //
    window.addEventListener('resize', onWindowResize, false );
				
    // postprocessing
    composer = new THREE.EffectComposer(renderer);
    composer.addPass(new THREE.RenderPass(scene, camera));

    glitchPass = new THREE.GlitchPass();
    glitchPass.renderToScreen = true;
    composer.addPass( glitchPass );
    updateOptions();
}

function onWindowResize() {

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize( window.innerWidth, window.innerHeight );

}

function onDocumentMouseMove( event ) {

    event.preventDefault();

    mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

}

And finally we want to add some code to change the color of the cubes when the user hovers over.

    function animate() {

        requestAnimationFrame( animate );

        render();
        composer.render();
				
    }

    function render() {

        theta += 0.1;

        camera.position.x = radius * Math.sin(THREE.Math.degToRad(theta ));
        camera.position.y = radius * Math.sin(THREE.Math.degToRad(theta ));
        camera.position.z = radius * Math.cos(THREE.Math.degToRad(theta ) );
        camera.lookAt(scene.position);

        camera.updateMatrixWorld();

        // find intersections

        raycaster.setFromCamera(mouse, camera);

        var intersects = raycaster.intersectObjects(scene.children);

        if (intersects.length > 0) {

            if (INTERSECTED != intersects[0].object) {

	        if (INTERSECTED) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex);

	            INTERSECTED = intersects[ 0 ].object;
                    INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
	            INTERSECTED.material.emissive.setHex( 0xff0000 );

	        }

	    } else {

                if (INTERSECTED) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );

                    INTERSECTED = null;

	        }

	        renderer.render( scene, camera );

	    }

        </script>

    </body>
</html>

And that is that, you now have an ultra cool landing page incorporating Three JS and a timer!

If you are stuck and need help, you can refer to the completed solution.

YouTube Video to this Tutorial:

Check out the GitHub for another example and visit Three.JS site for more examples of the amazing things the library is capable of. If you want even more of an overview the video above covers the project in more detail.

2
0

Related Posts