Thomas Czernek
Thomas Czernek

Reputation: 99

Why won't my images appear on the canvas?

I want to draw many different sized circles with the same image on them using a prototype. The problem is, the circles don't appear with the images drawn on them.

The circles are still there because they're visible when stroke is used, but the images aren't drawn. I was able to have an arc be drawn with an image in it without the prototype but as soon as I use it, it doesn't work.

No bugs appear in the console, so it is not clear to me what I'm missing. I've tried moving the event listener outside the prototype but the circle images still did not appear.

If anyone has any insight and perhaps a solution as to why this isn't working that they can share, that would be very much appreciated.

Here is the code:

const ctx = document.getElementById('canvas').getContext('2d');
class Balls {
  constructor(xPos, yPos, radius){
    this.xPos = xPos;
    this.yPos = yPos;
    this.radius = radius;
  }
}
Balls.prototype.render  = function(){
  const img = document.createElement('img');
  img.src = 'crystal.jpg';
  ctx.arc(this.xPos, this.yPos, this.radius, 0, Math.PI*2)
  ctx.stroke();	
  ctx.clip();
  img.addEventListener('onload', function(e){
    ctx.drawImage(img, this.xPos - this.radius, this.yPos - this.radius, 
    this.radius*2, this.radius*2);
  });
};
let object = new Balls(100, 100, 50);
object.render();
<canvas id='canvas'></canvas>

Upvotes: 2

Views: 1294

Answers (3)

Anurag Hazra
Anurag Hazra

Reputation: 413

This is an alternative way you can load images which is more modular, If you are making a game an Img class will come in handy.

Off-topic - if you want to load multiple images then you can add atotalLoadedcounter to keep track of how many images has been loaded and if thetotalLoadedcount is equal tototalImagescount then you can call a callback which will fire the animationLoop and preloads all the images.

const ctx = document.getElementById('canvas').getContext('2d');

// Img class to provide modular code
class Img {
  constructor(src, x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.src = src;
    this.img = new Image();
    this.img.src = this.src;
    this.isLoaded = false;
    this.img.addEventListener('load', (e) => {
      this.isLoaded = true;
      this.draw();
    });
  }

  draw() {
    if (this.isLoaded) {
      ctx.drawImage(this.img, this.x, this.y, this.size, this.size);
    }
  }
}

class Balls {
  constructor(xPos, yPos, radius) {
    this.xPos = xPos;
    this.yPos = yPos;
    this.radius = radius;
  }
}
Balls.prototype.render = function() {
  let imgx = this.xPos - this.radius;
  let imgy = this.yPos - this.radius;
  let imgr = this.radius * 2;
  let url = 'https://via.placeholder.com/150.png?text=Better';
  const img = new Img(url, imgx, imgy, imgr);
  ctx.arc(this.xPos, this.yPos, this.radius, 0, Math.PI * 2)
  ctx.stroke();
  ctx.clip();
  img.draw();
};
let object = new Balls(100, 100, 50);
object.render();
<canvas id='canvas'></canvas>

Upvotes: 1

vijay
vijay

Reputation: 926

Here you go, i just changed 2 things here in order to make your code work.

  1. load is used for eventlistener instead on onload.
  2. use Arrow function in order to rely on the parent context instead of dynamic context(this).

const ctx = document.getElementById('canvas').getContext('2d');
class Balls {
  constructor(xPos, yPos, radius) {
    this.xPos = xPos;
    this.yPos = yPos;
    this.radius = radius;
  }
}
Balls.prototype.render = function() {
  const img = document.createElement('img');
  img.src = 'https://via.placeholder.com/150.png?text=vijay'
  ctx.arc(this.xPos, this.yPos, this.radius, 0, Math.PI * 2)
  ctx.stroke();
  ctx.clip();
  img.addEventListener('load', (e) => {
    ctx.drawImage(img, this.xPos - this.radius, this.yPos - this.radius,
      this.radius * 2, this.radius * 2);
  });
};
let object = new Balls(100, 100, 50);
object.render();
<canvas id='canvas'></canvas>

Upvotes: 1

user2314737
user2314737

Reputation: 29307

You had two issues in your code. Firstly, the onload event should be load. Secondly, this is not known inside the event listener function, so this.radius etc. are all undefined and ctx.drawImage(img, this.xPos - this.radius, this.yPos - this.radius, this.radius*2, this.radius*2) doesn't work.

I used some variables for this.xPos, this.yPos, this.radius that can be used inside the event listener callback function. Hope this helps!

const ctx = document.getElementById('canvas').getContext('2d');
class Balls {
  constructor(xPos, yPos, radius) {
    this.xPos = xPos;
    this.yPos = yPos;
    this.radius = radius;
  }
}
Balls.prototype.render = function() {
  const img = new Image();
  img.src = 'https://source.unsplash.com/random';
  var xPos = this.xPos, yPos = this.yPos, radius = this.radius
  ctx.arc(xPos, yPos, radius, 0, Math.PI * 2)
  ctx.stroke();
  ctx.clip();
  img.addEventListener('load', function(e) {
    console.log("load")
    ctx.drawImage(img, xPos - radius, yPos - radius, 
    radius*2, radius*2);
  });
};
let object = new Balls(100, 100, 50);
object.render();
<canvas id='canvas'></canvas>

Upvotes: 1

Related Questions