Step 11

The constructor of the Ball class is not needed; if we eliminate it, the parent class's constructor (Sprite) is inherited. This behavior is different from, e.g., Java, where you must explicitly declare (non-default) constructors.

So, let's go ahead and delete the constructor of the Ball class. Moreover, let's its bounce method:

bounce(canvasWidth, canvasHeight) {
  if (this.x < 0 || this.x > canvasWidth) {
    // bounce off the left/right edges
    this.dx *= -1; // switch direction
  } 

  if (this.y < 0 || this.y > canvasHeight) {
    // bounce off the top/bottom edge
    this.dy *= -1; // switch direction
  } 
}

Save your code and observer the changes in the browser. Notice when the ball hits the right or bottom edge; it sinks into it slightly before changing direction! This behavior is caused as we don't account for the width and height of the ball. More concretely, we are always calculating the collision point of the canvas edge and the top left corner of the ball. So, let's refactor the code and address this issue!

bounce(canvasWidth, canvasHeight) {
  if (this.x < 0 || this.x + this.width > canvasWidth) {
    // bounce off the left/right edges
    this.dx *= -1; // switch direction
  } 

  if (this.y < 0 || this.y + this.height > canvasHeight) {
    // bounce off the top/bottom edge
    this.dy *= -1; // switch direction
  } 
}

Save your code and observer the changes in the browser.