onload isn't firing in the 'draw()' method, and my image is not getting drawn on the canvas. I have confirmed that the path is correct and the image is there.
I can't find where the problem is. Thank you in advance for advice.
class Game {
constructor() {
this.gameWidth = 600;
this.gameHeight = 300;
this.gravity = 0.7;
this.canvasName = "gameBox";
this.canvas = document.getElementById(this.canvasName)
this.ctx = this.canvas.getContext('2d');
}
draw(image) {
let img = document.createElement('IMG');
this.ctx.fillStyle ='#F00';
this.ctx.fillRect(0, 0, this.gameWidth, this.gameHeight);
img.onload = function() {
this.ctx.drawImage(img, 0, 0, image.width, image.height, image.xPos, image.yPos, image.width, image.height);
}.call(this);
img.src = image.imageFile;
}
}
class Sprite {
constructor(width, height, imageFile, xPos, yPos) {
this.width = width;
this.height = height;
this.imageFile = imageFile;
this.xPos = xPos;
this.yPos = yPos;
this.xVel = 0;
this.yVel = 0;
}
}
let game = new Game()
let elephant = new Sprite(21, 13, "./images/elephant.png", game.gameWidth / 2, game.gameHeight - 13);
game.draw(elephant);
CORRECT VERSION:
I've rewritten it taking in advice from guest271314, thank you! Still seems overly complex for my liking, but it works.
class Game {
constructor() {
this.gameWidth = 600;
this.gameHeight = 300;
this.gravity = 0.7;
this.canvasName = "gameBox";
this.canvas = document.getElementById(this.canvasName)
this.ctx = this.canvas.getContext('2d');
}
drawSprite(image, img) {
this.ctx.drawImage(img, 0, 0, image.width, image.height, image.xPos, image.yPos, image.width, image.height);
}
draw(image) {
let img = document.createElement('IMG');
this.ctx.fillStyle ='#F00';
this.ctx.fillRect(0, 0, this.gameWidth, this.gameHeight);
img.onload = this.drawSprite.bind(this, image, img);
img.src = image.imageFile;
}
}
class Sprite {
constructor(width, height, imageFile, xPos, yPos) {
this.width = width;
this.height = height;
this.imageFile = imageFile;
this.xPos = xPos;
this.yPos = yPos;
this.xVel = 0;
this.yVel = 0;
}
}
let game = new Game()
let elephant = new Sprite(21, 13, "./images/elephant.png", game.gameWidth / 2, game.gameHeight - 13);
game.draw(elephant);
.call() chained to the function is a syntax error without parenthesis wrapping the function. load callback should not be called until the load handler dispatches. You can define the function with a name and use .bind()
function handleImgLoad() {
// do stuff
}
img.onload = handleImgLoad.bind(this);
Related
I was successful in generating enemies (out of one image) with an array, however, I'm stuck in trying to generate enemies out of more than one image (e. g. 5 different images, thus 5 different enemies)
Here is my code that works:
Enemies (enemyImg - one image) are generated
/** #type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const backgroundImg = document.getElementById("background");
const playerImg = document.getElementById("player");
const enemyImg = document.getElementById("enemy");
canvas.width = 800;
canvas.height = 500;
const enemies = [];
class Enemy {
constructor(x, y, w, h, speed) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.speed = speed;
}
draw() {
ctx.drawImage(enemyImg, this.x, this.y, this.w, this.h);
}
update() {
this.x = this.x - this.speed;
}
}
function spawnEnemies() {
setInterval(() => {
let w = 100;
let h = 40;
let x = canvas.width;
let y = Math.random() * Math.abs(canvas.height - h);
let speed = 5;
enemies.push(new Enemy(x, y, w, h, speed));
}, 1000);
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, canvas.width, canvas.height);
enemies.forEach((enemy) => {
enemy.draw();
enemy.update();
});
}
animate();
spawnEnemies();
Here is the code, that does not work. I do not get any error message at all:
I have 6 images in one folder, named enemy_1.png to enemy_6.png) and I want them to be generated;
/** #type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const backgroundImg = document.getElementById("background");
const playerImg = document.getElementById("player");
const enemyImg = document.getElementById("enemy");
canvas.width = 800;
canvas.height = 500;
let enemies = [];
class Enemy {
constructor(img, x, y, w, h, speed) {
this.img = img;
(this.x = x),
(this.y = y),
(this.w = w),
(this.h = h),
(this.speed = speed);
}
draw() {
ctx.drawImage(img, this.x, this.y, this.w, this.h);
}
move() {
this.x = this.x - this.speed;
}
}
function createEnemies() {
setInterval(() => {
let w = 40;
let h = 72;
let x = 300;
let y = Math.random() * Math.abs(canvas.height - h);
let speed = 5;
enemies.length = 6;
for (let i = 1; i < enemies.length; i++) {
enemies[i] = new Image();
enemies[i].src = "./images/enemy_" + i + ".png";
enemies.push(new Enemy(enemies[i], x, y, w, h, speed));
}
}, 2000);
}
function createGame() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
enemies.forEach((enemy) => {
enemy.draw();
enemy.move();
});
requestAnimationFrame(createGame);
}
createGame();
createEnemies();
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const backgroundImg = document.createElement('img');
const playerImg = document.createElement('img');
canvas.width = 500;
canvas.height = 200;
// load your images:
const imagesCount = 0; // I have not this images, so its zero for me
const enemyImages = [];
for (let i = 1; i < imagesCount; i++) {
const img = new Image();
img.src = "./images/enemy_" + i + ".png";
enemyImages.push(img);
}
// I have not your images so i take some random pictures:
const enemyImage1 = new Image();
enemyImage1.src = 'https://pngimg.com/uploads/birds/birds_PNG106.png';
const enemyImage2 = new Image();
enemyImage2.src = 'https://purepng.com/public/uploads/large/purepng.com-magpie-birdbirdsflyanimals-631522936729bqeju.png';
const enemyImage3 = new Image();
enemyImage3.src = 'https://www.nsbpictures.com/wp-content/uploads/2018/10/birds-png.png';
enemyImages.push(
enemyImage1,
enemyImage2,
enemyImage3,
);
const enemies = [];
class Enemy {
constructor(x, y, w, h, speed, img) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.speed = speed;
// Self image:
this.img = img;
}
draw() {
// Draw self image:
ctx.drawImage(this.img, this.x, this.y, this.w, this.h);
}
update() {
this.x = this.x - this.speed;
}
}
function spawnEnemies() {
setInterval(() => {
let w = 60;
let h = 50;
let x = canvas.width;
let y = Math.random() * Math.abs(canvas.height - h);
let speed = 5;
enemies.push(new Enemy(x, y, w, h, speed,
// Pick random image from array:
enemyImages[Math.floor(Math.random()*enemyImages.length)]
));
}, 1000);
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, canvas.width, canvas.height);
enemies.forEach((enemy) => {
enemy.draw();
enemy.update();
});
}
animate();
spawnEnemies();
<canvas id=canvas></canvas>
I need help with Transform moving square image to circle in html canvas
I have imported a image that is a square. I want that image be round and still move.
Goal:
square image to circle image, and still able to move.
I have tried alot of tutorial on stackoverflow mainly with c. stroke and c.split but when I apply those the image doesnt move anymore.
Does someone have any suggestions?
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function Circle() {
//Give var for circle
this.x = 10;
this.y = 100;
this.dx = 1;
this.dy = 1;
this.radius = 50;
this.diameter = 2 * this.radius;
//Get external square picture (Needs to be converted in circle)
var image = new Image();
image.src = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
//Draw circle on canvas
this.draw = function () {
//Circle
c.beginPath();
c.arc(this.x, this.y, (this.radius*1), 0, Math.PI * 2, false);
c.closePath();
//TODO: cut square to circle
//Place square (image) on top of the circle
c.drawImage(image, (this.x-this.diameter/2) , (this.y-this.diameter/2), this.diameter, this.diameter);
};
//Update position
this.update = function () {
this.x += this.dx;
this.draw()
}
}
//Animate canvas
function animate() {
requestAnimationFrame(animate);
c.clearRect(0, 0, innerWidth, innerHeight);
this.update();
}
//Start
Circle();
animate();
canvas {
border: 1px solid black;
background: black;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas></canvas>
</body>
</html>
The answer is to use context.clip(); in your case c.clip(); this creates a clipping filter on the canvas that you can then draw in. Before you make a clip you must make a save and then restore after you draw with c.save(); and c.restore() respectively.
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
var circles = [];
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function Circle() {
//Give var for circle
this.x = 100;
this.y = 100;
this.dx = 1;
this.dy = 1;
this.radius = 50;
this.diameter = 2 * this.radius;
this.size = null;
this.c = null;
//Get external square picture (Needs to be converted in circle)
var image = new Image();
image.src = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
//Draw circle on canvas
this.draw = function () {
//Circle
this.c.beginPath();
this.c.arc(this.x, this.y, (this.radius*1), 0, Math.PI * 2, false);
this.c.closePath();
this.c.save();
this.c.clip();
//TODO: cut square to circle
//Place square (image) on top of the circle
this.c.drawImage(image, (this.x-this.diameter/2) , (this.y-this.diameter/2), this.diameter, this.diameter);
this.c.restore();
};
//Update position
this.update = function () {
if(this.x - this.radius <= 0 || this.x + this.radius >= this.size.x){this.dx = -this.dx}
this.x += this.dx;
this.draw()
}
this.init = function(options) {
Object.keys(options).forEach((key)=>{
this[key]=options[key];
})
}
}
//Animate canvas
function animate() {
requestAnimationFrame(animate);
c.clearRect(0, 0, innerWidth, innerHeight);
for(let i = 0; i < circles.length; i++){ circles[i].update(); }
}
//Start
for(let i = 0; i < 100; i-=-1){
let circle = new Circle();
circle.init({
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
size: {x: window.innerWidth,y: window.innerHeight},
c
})
circles.push(circle)
}
animate();
canvas {
border: 1px solid black;
background: black;
}
body {
margin: 0;
padding: 0;
overflow-x: hidden;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas></canvas>
</body>
</html>
Start by generating your composed image and then use it in your animation code.
To make such a compositing, you'd better use compositing than clipping, because clipping is an all or nothing operation, it won't deal well with the antialiasing required by a circle.
To store this composed image in a convenient way, you can either use a second canvas, or in newest browsers an ImageBitmap, both will then be drawable by drawImage.
Here is an ES5 implementation, using a second canvas and a callback:
// older browsers version
function makeCircleImage(radius, src, success, failure) {
var canvas = document.createElement('canvas');
canvas.width = canvas.height = radius * 2;
var ctx = canvas.getContext("2d");
var img = new Image();
img.src = src;
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// we use compositing, offers better antialiasing than clip()
ctx.globalCompositeOperation = 'destination-in';
ctx.arc(radius, radius, radius, 0, Math.PI*2);
ctx.fill();
success(canvas);
};
img.onerror = failure;
}
var url = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
makeCircleImage( 50, url, function( canvas ) {
document.body.appendChild(canvas);
}, console.error );
canvas {
border: 1px solid black;
background: black;
}
And here is one that you can use in newest browsers which will store an ImageBitmap (preferable when available for memory and theoretical speed of drawing).
// newest browsers version
function makeCircleImage( radius, src ) {
return new Promise( (resolve, reject) => {
// this canvas will get Garbage Collected
const canvas = document.createElement( 'canvas' );
canvas.width = canvas.height = radius * 2;
const ctx = canvas.getContext( "2d" );
const img = new Image();
img.src = src;
img.onload = function() {
ctx.drawImage( img, 0, 0, canvas.width, canvas.height );
// we use compositing, offers better antialiasing than clip()
ctx.globalCompositeOperation = 'destination-in';
ctx.arc( radius, radius, radius, 0, Math.PI*2 );
ctx.fill();
resolve( createImageBitmap( canvas ) );
};
img.onerror = reject;
});
}
async function init() {
const url = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
const img = await makeCircleImage(50, url);
document.querySelector('canvas')
.getContext('2d')
.drawImage(img, 0,0);
};
init().catch( console.error );
canvas {
border: 1px solid black;
background: black;
}
<canvas></canvas>
Now that we have a clear source ready to use, all our we have to do in the animation code is to put these pixels, without any heavy computation:
// older browsers version
function makeCircleImage(radius, src, callback) {
var canvas = document.createElement('canvas');
canvas.width = canvas.height = radius * 2;
var ctx = canvas.getContext("2d");
var img = new Image();
img.src = src;
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// we use compositing, offers better antialiasing than clip()
ctx.globalCompositeOperation = 'destination-in';
ctx.arc(radius, radius, radius, 0, Math.PI*2);
ctx.fill();
callback(canvas);
};
}
function Circle( x, y, radius ) {
//Give var for circle
this.x = x;
this.y = y;
this.dx = 1;
this.dy = 1;
this.radius = radius;
}
// use prototyping if you wish to make it a class
Circle.prototype = {
//Draw circle on canvas
draw: function () {
var
x = (this.x - this.radius),
y = (this.y - this.radius);
// draw is a single call
c.drawImage( this.image, x, y );
},
update: function () {
var
max_right = canvas.width + this.radius,
max_left = this.radius * -1;
this.x += this.dx;
if( this.x > max_right ) {
this.x += max_right - this.x;
this.dx *= -1;
}
if( this.x < max_left ) {
this.x += max_left - this.x;
this.dx *= -1;
}
},
init: function(callback) {
var url = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
makeCircleImage( this.radius, url, function(img) {
this.image = img;
callback();
}.bind(this));
}
};
//Animate canvas
function animate() {
c.clearRect(0, 0, window.innerWidth, window.innerHeight);
circles.forEach(function( circle ) {
circle.update();
});
circles.forEach(function( circle ) {
circle.draw();
});
requestAnimationFrame(animate);
}
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var circles = [
new Circle(10, 100, 50),
new Circle(10, 200, 30),
new Circle(10, 300, 50)
];
var ready = 0;
circles.forEach(function(circle) {
circle.init(oncircledone);
});
function oncircledone() {
if(++ready === circles.length) {
animate()
}
}
canvas {
border: 1px solid black;
background: black;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas></canvas>
</body>
</html>
And for newest browsers:
// newest browsers version
function makeCircleImage( radius, src ) {
return new Promise( (resolve, reject) => {
// this canvas will get Garbage Collected
const canvas = document.createElement( 'canvas' );
canvas.width = canvas.height = radius * 2;
const ctx = canvas.getContext( "2d" );
const img = new Image();
img.src = src;
img.onload = function() {
ctx.drawImage( img, 0, 0, canvas.width, canvas.height );
// we use compositing, offers better antialiasing than clip()
ctx.globalCompositeOperation = 'destination-in';
ctx.arc( radius, radius, radius, 0, Math.PI*2 );
ctx.fill();
resolve( createImageBitmap( canvas ) );
};
img.onerror = reject;
});
}
class Circle {
constructor( x, y, radius ) {
this.x = x;
this.y = y;
this.dx = 1;
this.dy = 1;
this.radius = radius;
}
draw() {
const x = (this.x - this.radius);
const y = (this.y - this.radius);
c.drawImage( this.image, x, y );
}
update() {
const max_right = canvas.width + this.radius;
const max_left = this.radius * -1;
this.x += this.dx;
if( this.x > max_right ) {
this.x += max_right - this.x;
this.dx *= -1;
}
if( this.x < max_left ) {
this.x += max_left - this.x;
this.dx *= -1;
}
}
async init() {
const url = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
this.image = await makeCircleImage( this.radius, url );
}
}
//Animate canvas
function animate() {
c.clearRect(0, 0, window.innerWidth, window.innerHeight);
circles.forEach( (circle) => circle.update() );
circles.forEach( (circle) => circle.draw() );
requestAnimationFrame(animate);
}
const canvas = document.querySelector('canvas');
const c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const circles = [
new Circle(10, 50, 50),
new Circle(10, 150, 30),
new Circle(450, 250, 80)
];
Promise.all(circles.map( (circle) => circle.init() ))
.then(animate)
.catch(console.error);
canvas {
border: 1px solid black;
background: black;
}
<canvas></canvas>
But if you are going to have a lot of Circle instances and that they will only differ in their radius, but share the same original image's source, then you might prefer to store a single high quality version of the composed image and just rescale it in each instance's draw call.
// newest browsers version
function makeCircleImage( radius, src ) {
return new Promise( (resolve, reject) => {
// this canvas will get Garbage Collected
const canvas = document.createElement( 'canvas' );
canvas.width = canvas.height = radius * 2;
const ctx = canvas.getContext( "2d" );
const img = new Image();
img.src = src;
img.onload = function() {
ctx.drawImage( img, 0, 0, canvas.width, canvas.height );
// we use compositing, offers better antialiasing than clip()
ctx.globalCompositeOperation = 'destination-in';
ctx.arc( radius, radius, radius, 0, Math.PI*2 );
ctx.fill();
resolve( createImageBitmap( canvas ) );
};
img.onerror = reject;
});
}
(async () => {
const url = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
const image = await makeCircleImage(250, url); // original size of the png image
class Circle {
constructor( x, y, radius ) {
this.x = x;
this.y = y;
this.dx = 1;
this.dy = 1;
this.radius = radius;
}
draw() {
const x = (this.x - this.radius);
const y = (this.y - this.radius);
// now they all use the same image
// with a bit more computation though due to resizing
c.drawImage( image, x, y , this.radius * 2, this.radius * 2);
}
update() {
const max_right = canvas.width + this.radius;
const max_left = this.radius * -1;
this.x += this.dx;
if( this.x > max_right ) {
this.x += max_right - this.x;
this.dx *= -1;
}
if( this.x < max_left ) {
this.x += max_left - this.x;
this.dx *= -1;
}
}
}
//Animate canvas
function animate() {
c.clearRect(0, 0, window.innerWidth, window.innerHeight);
circles.forEach( (circle) => circle.update() );
circles.forEach( (circle) => circle.draw() );
requestAnimationFrame(animate);
}
const canvas = document.querySelector('canvas');
const c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const rand = (n) => Math.random() * n;
const circles = Array.from({ length: 50 })
.map( () =>
new Circle(
rand(canvas.width),
rand(canvas.height),
rand(125)
)
);
animate();
})()
.catch(console.error);
canvas {
border: 1px solid black;
background: black;
}
<canvas></canvas>
Code isn't working, I need to randomly position a square component. I tried running some code, but nothing happens. Here's my code:
function component(width, height, color, x, y, type) {
this.type = type;
if (type == "image"){
this.image = new Image();
this.image.src = color;
}
this.score = 0;
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.gravity = 0;
this.gravitySpeed = 0;
this.update = function() {
ctx = myGameArea.context;
if (this.type == "image"){
ctx.drawImage(this.image,this.x, this.y, this.width,this.height);
} else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
}
function startGame() {
random = Math.floor((Math.random()*100) + 1);
document.write(random);
random2 = Math.floor((Math.random()*100) + 1);
document.write(random2);
square = new component(random, random2, "green", random, random2);
myGameArea.start();
return square
}
var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 450;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.interval = setInterval(updateGameArea, 1);
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function updateGameArea() {
myGameArea.clear();
square.update();
}
For some reason, the function startGame doesn't work correctly. Nothing happens when I run the code. I opened the console log as well, and no syntax errors registered.
You can get the code to work by applying it like this, for example:
function component(width, height, color, x, y, type) {
this.type = type;
if (type == "image"){
this.image = new Image();
this.image.src = color;
}
this.score = 0;
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.gravity = 0;
this.gravitySpeed = 0;
this.color=color;
this.update = function() {
random = Math.floor((Math.random()*100) + 1);
random2 = Math.floor((Math.random()*100) + 1);
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
randcolor=getRandomColor();
ctx = myGameArea.context;
if (this.type == "image"){
ctx.drawImage(this.image, random, random2, random,random2);
} else {
ctx.fillStyle = randcolor;
ctx.fillRect(random, random2, random, random2);
}
this.x=random;
this.y=random2;
this.width=random;
this.height=random2;
this.color=randcolor;
}
}
function startGame() {
random = Math.floor((Math.random()*100) + 1);
random2 = Math.floor((Math.random()*100) + 1);
square = new component(random, random2, "green", random, random2);
myGameArea.start();
return square
}
var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 450;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.interval = setInterval(updateGameArea, 1);
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function updateGameArea() {
myGameArea.clear();
square.update();
}
document.getElementById("start").addEventListener('click', startGame)
<div id="start">press to start</div>
In here I did three things:
I added a text that you can click
I added an event listener on this text that invokes the startGame() function
I got rid of the document.write() statements. I built this is Jsfiddle, which doesn't allow it, and it's considered bad practice anyway.
I've been trying to draw on a canvas using the new Ecma6 class system, however after alot of research it just doesn't want to work. I'm passing the context as a paramater of the draw function, even when I log context it doesn't say it's empty or undefined.
Can any of you nice people help me?
This is my code:
class Canvas {
constructor() {
this.canvas = document.getElementById('canvas');
this.context = this.canvas.getContext('2d');
this.width = this.canvas.width;
this.height = this.canvas.height;
this.components = [];
}
draw() {
this.context.clearRect(0, 0, this.width, this.height);
this.context.globalCompositeOperation = 'hard-light';
this.components.map(e => e.draw(this.context));
window.requestAnimationFrame(this.draw.bind(this));
}
add(e) {
this.components.push(e);
this.components.sort(function (a, b) {
return a.layer - b.layer;
});
}
listeners() {
window.addEventListener('resize', () => {
this.width = this.canvas.width;
this.height = this.canvas.height;
}, false);
}
init() {
this.listeners();
this.draw();
}
}
class CanvasElement {
constructor(x, y, height, width, layer) {
this.position = {
x: x,
y: y
}
this.height = height;
this.width = width;
this.layer = layer;
this.color = "grey";
}
draw(context) {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.width, this.height);
}
}
class CanvasImage extends CanvasElement {
constructor(x, y, image, context) {
super(x, y, image.width, image.height, 0);
let self = this;
this.image = new Image();
this.image.onload = function () {
self.imageReadyToUse = true;
self.width = this.width;
self.height = this.height;
}
this.image.src = image;
this.imageReadyToUse = false;
}
draw(context) {
if (this.imageReadyToUse) {
context.drawImage(this.image, this.x, this.y);
}
}
}
Your error lies on the position in the drawImage method.
Your this.x and this.y values are undefined.
Here is a working example:
var imageURL = "data:image/gif;base64,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";
class Canvas {
constructor() {
this.canvas = document.getElementById('canvas');
this.context = this.canvas.getContext('2d');
this.width = this.canvas.width;
this.height = this.canvas.height;
this.components = [];
}
draw() {
this.context.clearRect(0, 0, this.width, this.height);
this.context.globalCompositeOperation = 'hard-light';
this.components.map(e => e.draw(this.context));
window.requestAnimationFrame(this.draw.bind(this));
}
add(e) {
this.components.push(e);
this.components.sort(function (a, b) {
return a.layer - b.layer;
});
}
listeners() {
window.addEventListener('resize', () => {
this.width = this.canvas.width;
this.height = this.canvas.height;
}, false);
}
init() {
this.listeners();
this.draw();
}
}
class CanvasElement {
constructor(x, y, height, width, layer) {
this.position = {
x: x,
y: y
}
this.height = height;
this.width = width;
this.layer = layer;
this.color = "grey";
}
draw(context) {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.width, this.height);
}
}
class CanvasImage extends CanvasElement {
constructor(x, y, size, context) {
super(x, y, size.width, size.height, 0);
let self = this;
this.context = context;
this.image = new Image();
this.image.onload = function () {
self.imageReadyToUse = true;
self.width = this.width;
self.height = this.height;
self.draw();
}
this.image.src = imageURL;
this.imageReadyToUse = false;
}
draw() {
if (this.imageReadyToUse) {
console.log("YOU ARE UNDEFINED:", this.x, this.y);
this.context.drawImage(this.image, 0, 0, 61, 68);
}
}
}
var canvas = new Canvas();
var canvasImage = new CanvasImage(0, 0, {width:61, height:68}, canvas.context);
canvasImage.draw();
<canvas style="border:1px solid grey" id="canvas" width="400" height="400"></canvas>
sdsdc
So I found out what the problem was. Instead of using this.x and this.y, I had to use this.position.x and this.position.y.
Thank you for your help!
I'm trying to create background for every created particle.
Canvas pattern is not working properly. I'm getting this error >> "SyntaxError: An invalid or illegal string was specified"
HTML
<canvas id="canvas"></canvas>
JS
(function(){
window.onload = function(){
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d'),
particles = {},
particleIndex = 0,
particleNum = 1;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.width);
function Particle(){
this.x = canvas.width / 2;
this.y = 0;
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
this.gravity = 0.2;
particleIndex++;
particles[particleIndex] = this;
this.id = particleIndex;
this.test = 0;
this.maxLife = 100;
}
Particle.prototype.draw = function(){
this.x += this.vx;
this.y += this.vy;
this.vy += this.gravity;
this.test++;
if ( this.test >= this.maxLife ) {
delete particles[this.id];
};
var img = new Image();
img.src = 'img/aaa.png';
var pattern = ctx.createPattern(img,'repeat');
ctx.fillStyle = pattern;
ctx.fillRect(this.x, this.y, 20, 20);
};
setInterval(function(){
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < particleNum; i++) {
new Particle();
};
for(var i in particles) {
particles[i].draw();
}
},30)
}})();
I was trying to do this also with ctx.drawImage(), but picture was displayed one time only.
Any tips?:)
Fiddle
I think the issue is the image being loaded later than its used.
Inside your draw() you have:
var img = new Image();
img.src = 'img/aaa.png';
var pattern = ctx.createPattern(img,20,20);
It is a bad idea to create new images everytime you want to draw. I strongly suggest you create the img variable outside of the draw loop. Once you set the .src of an image, you have to wait until it is loaded to use it. There is an onload event you can use to let you know when its ready.
Here is an example:
var imgLoaded = false;
var img = new Image();
img.src = 'img/aaa.png';
img.onload = function() { imgLoaded = true; };
function draw() {
...
if (imgLoaded) {
var pattern = ctx.createPattern(img, 'repeat');
...
}
...
}