Only one circle is showing up, after running the following script. Why? How can I fix it?

Viewed 43

I tried to "drow" several "boubles" on the canvas. I wrote this little script, but after running it, only one circle is showing up. It's seems like it is drawing tha same "bouble" 100 times. Why?

This is my app.js:

var canvas = document.getElementById("cv");
let width = window.innerWidth * 0.98;
let height = window.innerHeight * 0.97;
canvas.width = width;
canvas.height = height;

var context = canvas.getContext("2d");

var boubles = [];

var createBouble = function() {
  this.x = width * Math.random();
  this.y = height * Math.random();
  this.color = getColor();
  this.radius = 30 + (Math.random() * 50);
//  console.log(this);
  return this;
}

var getColor = function() {
  return 'rgba(' + (Math.random() * 255) + ', ' + (Math.random() * 255) + ', ' + (Math.random() * 255) + ', ' + 0.3 + ')';
}

var generateBoubles = function(amount) {
  for (let i = 0; i < amount; i++) {
    boubles.push(createBouble());
  }
  drowBoules();
}

var drowBoules = function() {
  context.clearRect(0, 0, canvas.width, canvas.height);
  for (let i = 0; i < boubles.length; i++) {
    drowBouble(i);
  }
}

var drowBouble = function(index) {
  context.fillStyle = boubles[index].color;
  context.arc(boubles[index].x, boubles[index].y, boubles[index].radius, 0, 2 * Math.PI);
  context.fill();
}

generateBoubles(10);
<canvas id="cv"></canvas>

3 Answers

In the function

var createBouble = function() {
  this.x = width * Math.random();
  this.y = height * Math.random();
  this.color = getColor();
  this.radius = 30 + (Math.random() * 50);
//  console.log(this);
  return this;
}

When you console.log this it is always turning out to be the window object.

That is because it is being treated as a normal function.

You should use the new keyword when creating a new object.

i.e. it should be

var generateBoubles = function(amount) {
  for (let i = 0; i < amount; i++) {
    boubles.push(new createBouble()); // use new here
  }
  drowBoules();
}

You're calling createBouble() without any binding to this. When you do that, the value of this defaults to the window object, so every time you call createBouble() you are updating the same object with different values.

To solve this, you can bind the this to some object, like so:

createBouble.call({}); // Assignes an empty object to 'this'

Or, using the new keyword, which assigns a new object to the this and also returns it (so you can avoid the return this;):

const b = new createBouble();

However, I think a better approach will be to avoid using the this and return a new object:

const createBouble = () => ({
  x: width * Math.random(),
  y: height * Math.random(),
  color: getColor(),
  radius: 30 + (Math.random() * 50),
});

in createBouble function you must return object of values and in drowBouble function you must first do context.beginPath();

var canvas = document.getElementById("cv");
let width = window.innerWidth * 0.98;
let height = window.innerHeight * 0.97;
canvas.width = width;
canvas.height = height;

var context = canvas.getContext("2d");

var boubles = [];

var createBouble = function() {
  let x = Math.floor( width * Math.random());
  let y = Math.floor(height * Math.random());
  let color = getColor();
  let radius = 30 + Math.floor(Math.random() * 50);

  return {x,y,color,radius};
}

var getColor = function() {
  return 'rgba(' + Math.floor(Math.random() * 255) + ', ' + Math.floor(Math.random() * 255) + ', ' + Math.floor(Math.random() * 255) + ', ' + 0.3 + ')';
}

var generateBoubles = function(amount) {
  for (let i = 0; i < amount; i++) {
    boubles.push(createBouble());
  }
  drowBoules();
}

var drowBoules = function() {
  context.clearRect(0, 0, canvas.width, canvas.height);
  for (let i = 0; i < boubles.length; i++) {
    drowBouble(i);
  }
}

var drowBouble = function(index) {
  context.fillStyle = boubles[index].color;
  context.beginPath();
  context.arc(boubles[index].x, boubles[index].y, boubles[index].radius, 0, 2 * Math.PI);
  context.fill();
}

generateBoubles(10);
<canvas id="cv"></canvas>

Related