I already create my canvas on my page, however when I try to import an image on the canvas, it does not appear, here are the code I used. (the makeGameArea is a method I created to make a canvas on the webpage, the code of this method is not shown because it is so long to put it here)
Here are the following code that should have problem
var myGameArea2 = makeGameArea(700, 150, "myArea", "white");
var myContext2 = myGameArea2.getContext("2d");
var myImage = new drawImage(myContext2, "sonic.gif", 91, 97, 0, 0);
myImage.draw();
function drawImage(ctx, src, width, height, x, y){
this.image = new Image();
this.image.src = src;
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.draw = function(){
this.image.onload = function(){
ctx.drawImage(this.image, this.x, this.y, this.width, this.height);
}
}
}
Is there anything wrong with my drawImage() method?
HTMLImage.onload will fire only once per src setting.
Here you are attaching your handler in the draw method, which I guess will be called later on. At this moment, the event will already have fired, and hence will never be called, since it won't fire again unless you reset its src.
Fixed code block:
function drawImage(ctx, src, width, height, x, y){
this.image = new Image();
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.draw = function(){
ctx.drawImage(this.image, this.x, this.y, this.width, this.height));
};
// if you want it to draw as soon as possible
this.image.onload = this.draw.bind(this);
this.image.src = src;
}
Related
I am trying to create a small game to learn javascript.Everything works fine except my Rect class.As a matter of fact key_click works fine when it executes some console.log, but doesn’t display anything with tile.draw_tile() (nor gives any error).Could you help me?
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let Rect = class {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
draw_tile() {
c.fillStyle = "red";
c.fillRect(this.x, this.y, this.width, this.height);
}
}
var mouse = {
x: undefined,
y: undefined,
key_move: window.addEventListener('mousemove',(event)=>{
mouse.x = event.clientX;
mouse.y= event.clientY;
}),
key_click:window.addEventListener('click',(event)=>{
tile.draw_tile();
})
}
let tile = new Rect(mouse.x, mouse.y, 30, 30);
main = function(){
mouse.key_move;
mouse.key_click;
window.requestAnimationFrame(main);
}
window.requestAnimationFrame(main);
I want the x and y to increase by 1 in each requestAnimationFrame, it only increases once
https://jsfiddle.net/2mzb7axu/1/
update(ctx) {
ctx.beginPath();
ctx.moveTo(this.x, this.y);
this.x++
this.y++
ctx.lineTo(this.x, this.y);
ctx.lineWidth = 5;
ctx.stroke();
}
Your animate function is initiating the Line constructor with every update call.
Try initiating the Line class on the Display constructor, something like this:
constructor() {
this.canvas = document.querySelector("#canvas");
this.ctx = this.canvas.getContext("2d");
this.width = window.innerWidth;
this.height = window.innerHeight;
this.canvas.width = this.width;
this.canvas.height = this.height;
this.animateBound = this.animate.bind(this);
this.line = new Line();
}
and within your update function, call the existing Line's instance:
animate() {
this.line.update(this.ctx);
console.log('Animate called');
requestAnimationFrame(this.animateBound);
}
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>
Here you go, i just changed 2 things here in order to make your code work.
load is used for eventlistener instead on onload.
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>
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>
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>
I have no idea what is wrong. This is being made on a completely new site, so there are no cache problems, and the javascript console isn't returning errors. The canvas spans over the whole page, and is tagged properly. Here is the script:
<script type = "text/javascript">
var game = document.getElementById("game");
var context = game.getContext("2d");
var gamePieces = []
function gamePiece(width, height, color, x, y){
this.width = width;
this.height = height;
this.x = x;
this.y = y;
update = function(){
context.fillStyle = color;
context.fillRect(this.x, this.y, this.height, this.width);
}
gamePieces[gamePieces.length] = this
}
var p1 = gamePiece(50, 50, "blue", 0, 0);
function update(){
context.clearRect(0, 0, game.width, game.height);
for(var i = 0; i < gamePieces.length; i++){
gamePieces[i].update();
}
}
</script>
Nevermind. I figured it out.
Everything is running smoothly, I just needed to add another context.fillstyle and context.fillRect() statement. derpyderp.
I thought I'd have a little play around with Canvas animations, as I've not really done much with it before. So I forked this repo
Live demo
and decided my first stop would be to replace the 'red rect' with an image. Should be pretty straight forward I thought. So I'll just replace the ctx.rect(this.x, this.y, this.width, this.height); with a new Image()
For some reason, the image never appears.
Here is my Bird.draw method.
Bird.prototype.draw = function(ctx) {
ctx.beginPath();
ctx.rect(this.x, this.y, this.width, this.height);
ctx.fillStyle = this.fill;
ctx.fill();
base_image = new Image();
base_image.src = 'images/icon.jpg';
base_image.onload = function(){
console.log('loaded');
ctx.drawImage(base_image, this.x, this.y, this.width, this.height);
return ctx.fillText("" + this.score + "/" + this.highest, this.x, this.y - 2);
}
};
and a full working JSFiddle here
I've also tried swapping out he image.load for a pattern, but it still didn't load.
The only difference I find between your code and working code on a project I have is that I assign the url to image's src after defining image onload listener.
this.canvas.loadImage = function (image, x, y, w, h) {
var context = this.getContext("2d");
var myImage = new Image();
myImage.onload = function () { context.drawImage(myImage, x, y, w, h); };
myImage.src = image; //<-- only actual difference I see with your code
return myImage;
}
This works for me
Problem
this inside an image.onload refers to the image (not Bird) so your this.x & this.y are undefined.
Solution
Attach a reference to your Bird to your base_image:
base_image.Bird=this; // this is your Bird
So here some refactored code (not tested--some tweaking may be required!):
ctx.beginPath();
ctx.rect(this.x, this.y, this.width, this.height);
ctx.fillStyle = this.fill;
ctx.fill();
base_image = new Image();
base_image.Bird=this;
base_image.src = 'http://upload.wikimedia.org/wikipedia/commons/f/f6/Choice_toxicity_icon.png';
base_image.onload = function(){
ctx.drawImage(base_image,
this.Bird.x, this.Bird.y, this.Bird.width, this.Bird.height);
ctx.fillText("" + this.Bird.score + "/" + this.Bird.highest,
this.Bird.x, this.Bird.y - 2);
}
Second Problem
The draw function is being called repeatedly. Therefore you're loading the image repeatedly and causing these bad effects depending on how fast the browser can load each new smileyface:
In IE11 the smileyface remains on the page
In FF27 the smileyface initially appears on the page but disappears on mouse movement
In Chrome32 the smileyface is loaded and drawn but disappears immediately
Second Solution
Pull the image loading out of draw() and just do drawImage(mySavedSmileyFace) inside draw().