How To Animate A Box Flying Across The Screen with JavaScript

Right away, the problems with the CSS approach to animation become apparent: For one, the CSS animation applies to all div containers the same way at the same time. Granted, we could just apply different IDs to each div and animate them individually, but that seems like a lot of extraneous CSS and HTML code to brute-force the effect that we’re looking for. Secondly, the JavaScript would also start to get rather unwieldy rather quickly for similar reasons. The real answer here is to animate the objects using JavaScript instead. This will allows us to define random variables for speed and timing on the fly instead of using global parameters that would have to be overridden every time.

iteration3.js

function makebox() {
    var top=Math.random();
    var speed=Math.random();
    speed=speed*8000;
    top=top*600;
    document.getElementById("box1").style.top=top+"px";
    document.getElementById("box1").style.left="-50%";
    document.getElementById("box1").style.backgroundColor=getRandomColor();
    document.getElementById("box1").style.display="block";
    $("#box1").animate({left: "110%"}, speed);
}

We’re going to use the same HTML framework as before, only removing any CSS that defines animation and adding a script tag that calls the jQuery library. (jQuery makes animation a lot easier, reducing several lines of JavaScript to a single line.) Taking a look at the new JavaScript makebox() function, we’ll see a new variable, speed, that is defined as some random multiple of 8000. This value will be used to define how long the animation takes, effectively defining the speed of the div’s motion across the screen.

document.getElementById("box1").style.top=top+"px";
document.getElementById("box1").style.left="-50%";
document.getElementById("box1").style.backgroundColor=getRandomColor();
document.getElementById("box1").style.display="block";

This CSS block is effectively the same as we’ve seen previously, except that we’re now going to place the div with ID “box1” at a position off the left side of the screen. This -50% location is arbitrary for now, defining a spot where the div will (hopefully) be hidden to start (about 50% of the screen’s width beyond the left edge of the screen).

$("#box1").animate({left: "110%"}, speed);

This jQuery argument polls the HTML code for a div with the ID “box1”, then animates it from its starting position (defined in the preceding CSS block) to a position 110% the width of the screen from the left edge (or about 10% the width of the screen right of the right edge). Note that jQuery animations have to be defined relative to the initial state, otherwise unexpected things may happen depending on the way the browser chooses to render the result (in other words, you can’t/shouldn’t animate from a “left” position to a “right” position). The speed variable defines the duration of the animation, in miliseconds. For this example, the animation will take anywhere from 0-8 seconds.

Note that the animation in this iteration does not loop (we’ll address that in the next iteration), so you may need to refresh the page.

How To Animate Multiple Boxes Flying Across The Screen Using jQuery

This fourth iteration is where things are going to start coming together. We have the basic structure of a colored div container moving across the screen from different Y positions and at different speeds. Now, we’re going to clean up the code and start to customize it to our particular application.

iteration4.html

<html>
<head>
<script type="text/javascript" src="iteration4.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<style>
    body {
        overflow: hidden;
    }

    div.window {
        position: absolute;
        background-color: black;
        left: -427px;
        width: 427px;
        height: 320px;
    }

    div.window img {
        width: 100%;
        height: inherit
    }
</style>

</head>
<body>
<script>
$(document).ready(function(){
       makebox();
    });
</script>

<div class="window"></div>

</body>
</html>

In this HTML code, we’ve moved up from a 200px square to a 427x320px rectangle that will more closely fit the thumbnail images that we’re going to be using to fill the “flying windows”. We’ve also confined the viewport to just the visible size of the screen (no more scroll bar) and moved the starting position of the animated divs to just off the left side of the screen. Images in the “window” divs will take up the entire size of the container.

iteration4.js

function makebox() {
    $(".window:not(:animated)").each(function() {
        $(this).css({"top": Math.random() * window.innerHeight - 160,"left": "-500px","backgroundColor": getRandomColor});
        $(this).animate({left: "800px"}, Math.random() * 5000 + 5000, makebox);
    });
}

Thanks to some jQuery, we are able to reduce the length of the function by about half while adding some more sophisticated behavior!

$(".window:not(:animated)").each(function() This initial line tests whether or not each div container of class “window” is currently being animated. If it’s not being animated, then the attached function will run.

$(this).css({"top": Math.random() * window.innerHeight - 160,"left": "-500px","backgroundColor": getRandomColor}); The this jQuery object will apply the subsequent code to each matching object from the previous test. In this case, we’ll apply new CSS properties to the particular div: a starting Y position that’s some random fraction of the current viewport’s height (minus half of the height of the div to make sure it appears on the screen), a starting X position that’s 500 pixels left of the left edge, and a random background color (defined by the getRandomColor() function that hasn’t changed).

$(this).animate({left: "800px"}, Math.random() * 5000 + 5000, makebox); This version of the animate method will move the current div container across the screen to a position 800 pixels from the left edge (the right edge of the iframe). The animation will take some random time between 5 and 10 seconds to complete.

If we add more div containers to the HTML document, we get more windows flying across the screen:

Adding Random Images To Flying Windows Using jQuery

Now that we have the completed application skeleton, we need to populate the div containers with randomly selected thumbnails from the videos we’ll be viewing. The first thing we’ll need to do is add an array populated by the unique YouTube identifier locations to our JavaScript file. Again, I have omitted the getRandomColor() function because it has not changed.

iteration5.js

const addresses = ["9KuZj8zb0pQ",
"8amHoqOTsyI",
"g4ywxRCztuE",
"JMtzR9JpGFI",
"PdKB6opHjSM",
"bn9SLbfOEwo",
"IoIdD1p21o8",
"LusQqtxaCb8",
"Ds-Vp29bBcs",
"Q3fvTEsT0EI",
"fN-9U_9xaR4",
"HL_8u_qwIf0",
"ho5iDlsUSQE",
"gTHTsJfmY8k",
"Uegvzyl-wEU",
"b3G2QRE9qtY",
"rkFbx59lqEI",
"tFPuceqF37E",
"8wqAnj6Skjk",
"dsucNSCufJs",
"jh5lP3nmgC4",
"mNM0jy58gT8",
"rNkr_otHZUs",
"IDwWUHJ9PGk",
];
        
function makebox() {
    $(".window:not(:animated)").each(function() {
        addy = (Math.floor(Math.random() * addresses.length));
        $(this).css({"top": Math.random() * window.innerHeight - 160,"left": "-"+Math.random() * 150 -150+"%","background-image": "url('https://img.youtube.com/vi/"+addresses[addy]+"/0.jpg')", "border": "10px solid", "color": getRandomColor});
        $(this).animate({left: "150%"}, Math.random() * 5000 + 5000, makebox);
    });
}

Again, the makebox() function begins with a test to see which divs on screen are animated and then sets the initial position in CSS before animating the div to move across the screen from left to right. However, we have added the new variable addy which is to be populated by a random number between 0 and the amount of values in the addresses array (by using the Math.floor() function, we round the random number down to the nearest integer).

$(this).css({"top": Math.random() * window.innerHeight -
160,"left": "-"+Math.random() * 150 -150+"%","background-image":
"url('https://img.youtube.com/vi/"+addresses[addy]+"/0.jpg')", "border":
"10px solid", "color": getRandomColor});
We’re making three significant changes to this CSS object in this iteration: First, we’re changing the starting X position of the div to a random value between 150% and 300% of the screen width left of the left edge. Second, we’re adding a 10 pixel border to the div in a random color. Third, we’re adding a new background image to the div’s CSS parameters by calling the thumbnail image stored on YouTube’s server for the video located at the address string stored in the addresses array at position addy.

$(this).animate({left: "150%"}, Math.random() * 5000 + 5000, makebox); The last change in this version of the function is to call the makebox() function once the animation is finished.

iteration5.html

<html>
<head>

<script type="text/javascript" src="iteration5.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<style type="text/css">

    body {
        overflow: hidden;
    }

    div.window {
        position: absolute;
        background-color: black;
        left: -427px;
        width: 427px;
        height: 320px;
        justify-content: center;
        align-items: center;
    }

    div.window img {
        width: 95%;
        height: 95%;
    }

</style>
</head>

<body>

<script>
$(document).ready(function(){
       makebox();
    });
</script>

<div class="window"></div>

</body>
</html>

We’ve only made minor changes to the CSS section of the HTML file. Since we want the images centered in the div container, we add the justify-content: center and align-items: center parameters to the window class div stylesheet and we resize the img elements within the window class divs to 95% of their original size. Everything else runs as the previous iteration (I did omit the extra divs, but they can be added back in as needed to populate the screen more fully.

How To Link To A Popup Video Player On A Website

The VHS Time Capsule application is nearly complete; we just need to have some kind of popup video player that opens whenever a thumbnail is clicked. Of course, it also has to play the corresponding video. Thankfully, since we’re using videos hosted on YouTube, we only need one set of location strings. It’s just going to be a little tricky to embed them in the format we need.

iteration6.html

<html>
<head>
<script type="text/javascript" src="iteration6.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<style type="text/css">
    body {
        overflow: hidden;
    }

    div.window {
        position: absolute;
        background-color: black;
        left: -427px;
        width: 427px;
        height: 320px;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    #viewer {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 740px;
        height: 580px;
        visibility: hidden;
        background-color: black;
        z-index: 100;
    }

</style>
</head>

<body>

<script>
$(document).ready(function(){
       makebox();
    });
</script>

<div id="viewer"></div>

<div class="window"></div>

</body>
</html>

In the HTML file, we’ve added a new stylesheet for a div with the ID “viewer” that will appear in the center of the viewport. The transform: translate(-50%, -50%) parameter centers the div by moving its origin point (the top left corner) upward and leftward by 50% of the element’s height and width, respectively. The z-index defines the “distance off the paper” that the div will be drawn: higher numbers indicate closer to the viewer. At z-index 100, the viewer should appear on top of everything else being drawn. This new viewer div will start hidden and be made visible through JavaScript.

iteration6.js

function makebox() {
    $(".window").click(function () {
        color = $(this).css("color");
        $("#viewer").css({"visibility": "visible", "background-color": color});
        video = $(this).css("background-image");
        video = video.slice(32, -6);
        parent.document.getElementById("viewer").innerHTML=""<img src='close.png' style='position:absolute; right:25px; top: 25px; transform: translate(30%, -25%);' onclick='hideViewer()'><iframe width='640' height='480' style='position:absolute; top:50px; left:50px;' src='https://www.youtube.com/embed/"+video+"?controls=0&autoplay=1&fs=0&modestbranding=1&rel=0' title='YouTube video player' frameborder='0' allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture' allowfullscreen></iframe>";
    });
    $(".window:not(:animated)").each(function() {
        addy = (Math.floor(Math.random() * addresses.length));
        $(this).css({"top": Math.random() * window.innerHeight - 160,"left": "-"+Math.random() * 150 -150+"%","background-image": "url('https://img.youtube.com/vi/"+addresses[addy]+"/0.jpg')", "border": "10px solid", "color": getRandomColor});
        $(this).animate({left: "150%"}, Math.random() * 15000 + 15000, makebox);
    });
}

function hideViewer() {
    $("#viewer").css({"visibility": "hidden"});
    parent.document.getElementById("viewer").innerHTML="";
}

Our trusty makebox() function has become quite complicated now, so let’s go over the new additions line-by-line:

$(".window").click(function () { When a div of class “window” is clicked, the embedded function will be invoked as follows:

color = $(this).css("color"); A new variable, color, is defined by the CSS color attribute for the particular div we’re working with. This color attribute will be set by the getRandomColor() function when the div is drawn.

$("#viewer").css({"visibility": "visible", "background-color": color}); This makes the viewer div visible and colors it with the same as the border of the div that was clicked.

video = $(this).css("background-image"); This defines a new variable video and defines it as the URL of the thumbnail populating the div that was clicked.

video = video.slice(32, -6); Redefines video by eliminating the first 32 characters and the last 6 characters of the value defined in the preceding line. This effectively reverts video back to the YouTube location identifier that would be found in the addresses array so that we can use it in the next line.

parent.document.getElementById("viewer").innerHTML="<img
src='close.png' style='position:absolute; right:25px; top: 25px;
transform: translate(30%, -25%);' onclick='hideViewer()'><iframe
width='640' height='480' style='position:absolute; top:50px; left:50px;'

src=’https://www.youtube.com/embed/”+video+”?controls=0&autoplay=1&fs=0&modestbranding=1&rel=0′
title=’YouTube video player’ frameborder=’0′ allow=’accelerometer;
autoplay; clipboard-write; encrypted-media; gyroscope;
picture-in-picture’ allowfullscreen></iframe>”;

This injects the defined HTML code between the tags for the “viewer” div. The code here is the boilerplate YouTube iframe embed code with a few optional parameters specified. The HTML also centers the YouTube iframe and renders a close button in the corner of the #viewer div that will run the hideViewer() function when clicked.

The rest of the makebox() function doesn’t change, but we do add a new hideViewer() function to the script.

function hideViewer() {
    $("#viewer").css({"visibility": "hidden"});
    parent.document.getElementById("viewer").innerHTML="";
}

This new function simply hides the viewer div by changing the CSS and stops any video playing by clearing the HTML inside the #viewer div tags.

How To Detect Browser User Agent And Add Mobile Website Support

There remains one major problem with the application as it stands right now: when viewed on a mobile browser, the animation breaks down and the viewer window causes undesirable effects such as rescaling the page. As such, we’ll need to tweak the operation just slightly for mobile browsers (this wasn’t such a problem in the 1990s and early 2000s, but with most web browsing done via mobile, it would be dumb not to account for the difference.

iteration7.html

<script>
$(document).ready(function(){
       detectUserAgent();
    });
</script>

In the main HTML file, the only change we’re going to make is calling a different function from our JS file. In this case, it’s a function that will detect the user agent, letting the application know whether to run the desktop or mobile version of the app.

iteration7.js

function detectUserAgent() {
    if ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) || (navigator.userAgent.match(/Android/i))) {
        location.replace("mobile.html");
    }
    startplayer();
    }

function startplayer() {
    $.getScript("videofiles.js", function() {
        });
    makebox();
    }

In the JavaScript, the new detectUserAgent() function queries the navigator.userAgent object to see if it contains the string “iPhone”, “iPad”, or “Android” which would indicate a mobile device and browser. If one of those strings is found in the object, then the current page is redirected to “mobile.html”. Otherwise, the startplayer() function is called.

In the startplayer() function, we use the jQuery getScript() method to load another JS file within the script as running. In this case, it will load the “videofiles.js” file that now contains the addresses array.

The “mobile.html” file works exactly like the main HTML file, except that it now refers to a slightly different JS file, “mobile.js” that contains the changes we need to make the mobile version work.

mobile.js

function makebox() {
    $(".window").click(function () {
        video = $(this).css("background-image");
        video = video.slice(32, -6);
        window.open("https://www.youtube.com/embed/"+video, '_blank');
    });
    $(".window:not(:animated)").each(function() {
        addy = (Math.floor(Math.random() * addresses.length));
        $(this).css({"top": Math.random() * window.innerHeight - 290+"px","left": "-"+Math.random() * 740 - 740+"px","background-image": "url('https://img.youtube.com/vi/"+addresses[addy]+"/0.jpg')", "border": "10px solid", "color": getRandomColor});
        $(this).animate({left: "2000px"}, Math.random() * 15000 + 15000, makebox);
    });
    }

We only need to make two major changes to the makebox() function to make it work on mobile: First, we need to make sure that we define all animations by pixel distances instead of screen widths. The percentage screen width method of measurement does not work correctly on some mobile browsers and can cause the flying windows to rescale themselves when they reach the right edge of the screen.

window.open("https://www.youtube.com/embed/"+video, '_blank');
Second, instead of a viewer div playing an embedded YouTube video, we’re going to open the video in a new tab. We’re still going to use the embed version of the video, though, to avoid having the full YouTube player page load (which would also be undesirable).

From this point, we just add an appropriate background video to the HTML file body using the background-video CSS property, some appropriate MIDI music using the MIDIjs JavaScript library (midijs.net), and a few navigation buttons, and the application is ready to go!