Make image bounce around container

I am trying to make a object bounce around a container as apart of a project I am working on. The objects are image elements that are designed to look like bubbles. Over time the images will decrease in opacity and then be deleted.

My issue is that the bubbles will spawn into the container and decrease in opacity but never move. The code to delete the bubble also is erroring out. Since each bubble has no unique ID, I try to delete each one using the this keyword to try and delete itself.

I set the bubbles to delete after < 0.8 opacity to speed up testing.

The instructions I was supposed to following can be found in this book.

  • New Perspectives on HTML5, CSS3, and JavaScript
  • Chapter 14: Exploring Object-Based Programming: 14-13b Apply: Case Problem
  • Link to instructions

I tried to snippet out the code that is relevant. I also kept the links in the head.

This is all very new to me since this is advanced and the instructions tell me what to type and not how it entirely works. Sorry if I can give so little information on why it isn’t working.

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
   <title>Party Supplies</title>
   <meta charset="utf-8" />
   <link href="bu_layout.css" rel="stylesheet"  />
   <script src="bu_bubbles.js" async></script>
</head>
<body>
   <header>
      <section id="logosection">        
<!-- Continar bubbles will bounce around in -->
	  <div id="bubbleBox"></div>
      </section>  
   </header>
</body>
</html>

CSS:

body > header > section {
   position: relative;
   background: radial-gradient(rgba(235, 200, 136, 0.6) 40%, rgba(51, 51, 51, 0.8));

   width: 1024px;
   height: 500px;
}

JS:

// Define the width and height of the container
var box = {
	width: 1024,
	height: 500
}

// Define a single bubble object
function bubble(radius, imageURL) {
	this.radius = radius;
	this.imageURL = imageURL;
	this.xVelocity = null;
	this.yVelocity = null;
	this.xPos = null;
	this.yPos = null;
	this.opacity = 1;
	this.hue = 0;
	this.rotate = 0;
	this.rotateDirection = 1;
	// decrease opacity over time
	this.fadeBubble = function() {
		this.opacity -= 0.0005;
		console.log(this.opacity)
	}
	// change HUE over time (not working)
	this.changeColor = function() {
		this.hue = (this.hue + 3) % 360;
	}
	// rotate bubble (not working)
	this.rotateBubble = function() {
		this.rotate =  (this.rotate + this.rotateDirection) % 360;
	}
	// move bubble (not working)
	this.moveBubble = function(height, width) {
		console.log("Bubble Moved");
		var bubbleTop = this.yPos;
		var bubbleBottom = this.yPos + this.radius;
		var bubbleLeft = this.xPos;
		var bubbleRight = this.xPos + this.radius;
		if (bubbleTop < 0 || bubbleBottom > height) {
			this.yVelocity = this.xVelocity;
		}
		if (bubbleLeft < 0 || bubbleRight > width) {
			this.xVelocity = this.yVelocity;
		}
		this.yPos = this.yVelocity;
	}
}

window.addEventListener("load", function() {
   // Reference to the bubble box
   var bubbleBox = document.getElementById("bubbleBox");
   
   // Create a new bubble every half-second
   setInterval(function() {
      
      // Do not create more than 20 bubbles at any one time
      if (bubbleBox.childElementCount <= 20) {
		  
		  // create a new bubble and define values
         var newBubble = new bubble(randInt(50,120), "bu_bubble"+randInt(1,10)+".png");
		 newBubble.xPos = box.width/2
		 newBubble.yPos = box.height/2
		 newBubble.xVelocity = randInt(5,5)
		 newBubble.yVelocity = randInt(5,5)
		 newBubble.rotate = randInt(0, 360);
		 newBubble.hue = randInt(0, 360);
		 newBubble.rotateDirection = randInt(2,2);
		 console.log(newBubble);
		 
		 // create physical bubble image
		 var bubbleImg = document.createElement("img")
		 bubbleImg.setAttribute("position", "abosulte");
		 bubbleImg.setAttribute("src", newBubble.imageURL);
		 bubbleImg.style.width = (newBubble.radius+"px");
		 bubbleImg.style.left = (newBubble.xPos+"px");
		 bubbleImg.style.top = (newBubble.yPos+"px");
		 // add bubble image to html
		 document.getElementById("bubbleBox").appendChild(bubbleImg);
		 
		 // interval to update and move bubble image every 25 miliseconds
		 var bubbleInterval = setInterval(function() {
			 newBubble.fadeBubble();
			 // remove bubble if its too low in opacity
			 if (newBubble.opacity < 0.8) {
				 document.getElementById("bubbleBox").removeChild(this)
				 clearInterval(bubbleInterval);
				 // otherwise run animation functions
			 } else {
				 bubbleImg.style.opacity = newBubble.opacity
				 newBubble.changeColor();
				 bubbleImg.style.filter = "hue-rotate("+newBubble.hue+")"
				 newBubble.rotateBubble();
				 bubbleImg.style.transform = "rotate("+newBubble.rotate+")"
				 newBubble.moveBubble(box.height, box.width)
				 bubbleImg.style.top = newBubble.yPos
				 bubbleImg.style.left = newBubble.xPos
				 console.log(newBubble.xPos, newBubble.yPos);
			 }
			 
		 }, 25);
      }
      
   }, 500);

   /* Function to return a random integer between minVal and maxValue inclusive */
   function randInt(minVal, maxVal) {
      var size = maxVal - minVal + 1;
      return Math.floor(minVal + size*Math.random());
   }  
});

Hello there,

Just a few things to think about:

	this.xVelocity = null;
	this.yVelocity = null;
	this.xPos = null;
	this.yPos = null;
  1. You are initialising these bubbles with no velocity, and no position. So, what do you expect this to do:
bubbleImg.style.left = (newBubble.xPos+"px");
bubbleImg.style.top = (newBubble.yPos+"px");

What you should be thinking about here is:

  • Position and velocity are likely going to be used as numbers. So, they should be initialised with numbers. This could be 0, or the centre of the screen, or a random position.
  1. The bubbles are all being initialised within the setInterval function with the same name.
  • This always creates bubbles, every iteration.
  • This does not always remove bubbles.
  • However, the names are all the same. So, keeping track is more difficult, if not impossible.

Here is some sudo-code of what I would do (take ideas from it):

function Bubbles(r, imageURL) { // FYI: It is common practice to name class-like objects with upper-camel case
    this.radius = r;
    this.x = 0;
    this.y = 0;
    this.imageURL = imageURL;
    this.dxdt = 0; // x-component velocity
    this.dydt = 0; // y-component velocity
    this.opacity = 1;

    this.show = function() {
        // Logic to show the bubble
        // Consider decreasing the opacity here
    }
    this.move = function() {
        // Logic to move the bubble
    }

}

function main() {
    let bubbles = []; // An array to hold all the bubbles;

    // Create a few bubbles
    for (let i = 0; i < 100; i++) {
        bubbles.push(new Bubbles(10, someImage));
    }
    // While there are bubbles to show, play the loop
    while (bubbles.length > 0) {
        for (let bubble of bubbles) {
            bubble.show();
            bubble.move();
            
            // Remove bubble from array
            if (bubble.opacity < 0.8) {
                bubbles.splice(bubbles.indexOf(bubble));
            }
        }
    }
}

P.S. there are some cracking tutorials on YouTube doing exactly this.

Hope this helps.

Hey, Thank you for your help.

The reason the things were done like that was because I was required to following these set instructions (link) that was supposed to guide me through it all and tell me what code to write (school assignment).

Here is a download for all of the starter files

This is the code they give me originally JS wise:

"use strict"

window.addEventListener("load", function() {
   // Reference to the bubble box
   var bubbleBox = document.getElementById("bubbleBox");
   
   // Create a new bubble every half-second
   setInterval(function() {
      
      // Do not create more than 20 bubbles at any one time
      if (bubbleBox.childElementCount <= 20) {
         
      }
      
   }, 500);

   /* Function to return a random integer between minVal and maxValue inclusive */
   function randInt(minVal, maxVal) {
      var size = maxVal - minVal + 1;
      return Math.floor(minVal + size*Math.random());
   }  

});

I am supposed to follow those instructions and somehow get the result of the moving bubbles.

I do not understand it myself.

Ah right. I should have spent time reading the instructions. It is odd to set the positions to null.

Just to mention it: Have a think about this section

if (bubbleTop < 0 || bubbleBottom > height) {
	this.yVelocity = this.xVelocity;
}
if (bubbleLeft < 0 || bubbleRight > width) {
	this.xVelocity = this.yVelocity;
}

The instructions appear to be formatted incorrectly for me. So, this is what I think they should say:

If bubbleTop is less than zero or bubbleBottom is greater than the height argument, then the bubble has hit the top or bottom wall; if so, change the direction of the vertical velocity by letting
this.yVelocity = -this.yVelocity

Finally, think about how you are keeping track of each bubble, so when you refer to one, how do you know you are referring to the correct one…

1 Like