How to make multiple elements in a canvas - javascript

I'm trying to show multiple pipes in the canvas of same height but even after using a for loop it is showing a single pipe and not a lot of it
<script>
var tryCanvas = document.getElementById("myCanvas");
var c = tryCanvas.getContext("2d");
var myCall = [];
function Squares() {
for(var i =0; i < 10; i++){
this.x = Math.random()* tryCanvas.clientWidth;
this.y = 0;
this.w = 20;
this.h = 60;
this.counter = 0;
this.draw = function() {
c.beginPath();
c.rect(this.x, this.y, this.w, this.h)
c.fill();
}
}
this.update = function() {
if(this.x < 0){
this.x = 0;
}
this.x -= 1;
this.draw();
}
}
var holder = new Squares;
setInterval(callFun, 10);
function callFun() {
c.clearRect(0,0,tryCanvas.clientWidth, tryCanvas.clientHeight);
holder.update();
}
</script>
If I push the constructor function in an array it's not showing anything in the canvas and in the console it's giving undefined or NaN.
But if I do it without "this" its generating the number of rects.
What am I doing wrong here?

Updated to move the bars along the screen
See this working example:
https://codepen.io/bkfarns/pen/braWQB?editors=1010
This.draw will only get created with the values from the last iteration of the for loop.
Also as a side node, usually instead of new Squares you call the constructor like new Squares(). When you call the constructor you are calling a method.
But I think the code below fixes your issues. Try it out:
<body>
<canvas id="myCanvas"/>
</body>
<script>
var tryCanvas = document.getElementById("myCanvas");
var c = tryCanvas.getContext("2d");
var myCall = [];
function Squares() {
this.draw = function(xOffset) {
for(var i =0; i < 10; i++){
this.x = (i * xOffset) + (5*i)//Math.random()* tryCanvas.clientWidth;
this.y = 0;
this.w = 20;
this.h = 60;
c.beginPath();
c.rect(this.x, this.y, this.w, this.h)
c.fill();
}
}
}
var holder = new Squares();
var xOffset = 20;
setInterval(function() {
c.clearRect(0,0,tryCanvas.clientWidth, tryCanvas.clientHeight);
holder.draw(xOffset)
xOffset--;
}, 1000)
</script>

Related

I cant figure out how to make my loop work in my space invader like game in Javascript

For my space invader like game I want to make bullets that get shot by the enemies but the problem is that the bullets dont appear in this loop that I made. I tried several things like making only one bullet appear while that works it is not the result that I want. My idea behind the code is that a new bullet gets shot from the position of the enemies when the enemybullet.position == 1 and if it exceeds the height of the canvas I want the bullet to return to the enemies and be shot again.
The code that I used for this result is here:
sketch.js
var enemies = [];
var enemybullet = [];
function setup() {
createCanvas(800, 600);
for (var i = 0; i < 2; i++) {
enemies[i] = new Enemy(i*300+300, 100);
}
// I tried enemybullet = new Enemybullet(120, 200); and that drew the bullet but it wasnt assigned to the enemy
rectMode(CENTER);
}
function draw() {
background(0);
// enemybullet.show(); this is wat I used to draw the single projectile
// enemybullet.move();
var edge = false;
for (var i = 0; i < enemies.length; i++) {
enemies[i].show();
enemies[i].move();
if(enemies[i].x > width || enemies[i].x < 0) {
edge = true;
}
}
if (edge) {
for (var i = 0; i < enemies.length; i++) {
enemies[i].shiftDown();
}
}
for (var i = 0; i < enemybullet.length; i++) {
enemybullet[i].show();
enemybullet[i].move();
for (var j = 0; j < enemies.length; j++) {
if(enemybullet[i].position == 1){
var enemybullets = new Enemybullet(enemies[j].x(), enemies[j].y());
enemybullet.push(enemybullets);
enemybullet[i].x = enemybullet[i].x;
enemybullet[i].y = enemybullet[i].y + enemybullet[i].speed;
if(enemybullet[i].y >= height){
enemybullet[i].position = 2;
}
}
else{
enemybullet[i].y = enemies[i].y;
enemybullet[i].x = enemies[i].x;
}
if(enemybullet[i].position == 2 ){
enemybullet[i].y = enemies[i].y;
enemybullet[i].x = enemies[i].x;
enemybullet[i].position = 1;
}
}
}
enemybullet.js
function Enemybullet(x, y) {
this.x = x;
this.y = y;
this.width = 10;
this.height = 20;
this.position = 1;
this.speed = 2;
this.show = function() {
fill('#ADD8E6');
rect(this.x, this.y, this.width, this.height);
}
this.move = function() {
this.x = this.x;
this.y = this.y + this.speed;
}
}
enemy.js
function Enemy(x, y) {
this.x = x;
this.y = y;
this.r = 100;
this.xdir = 1;
this.shot = function() {
this.r = this.r * 0;
this.xdir = this.xdir * 0;
}
this.shiftDown = function() {
this.xdir *= -1;
this.y += this.r/2;
}
this.show = function() {
fill('#0000FF');
rect(this.x, this.y, this.r, this.r);
}
this.move = function() {
this.x = this.x + this.xdir;
}
}
Rather than having the bullets be in their own global array, try having the bullets be a variable in the Enemy class.
this.bullet = new Enemybullet(this.x, this.y);
Then you can give the enemies functions such as the following to update the bullets.
this.updateBullet = function() {
this.bullet.move();
this.bullet.show();
}
this.resetBullet = function() {
this.bullet = new Enemybullet(this.x, this.y);
}
Where you were looping through each bullet before, you can instead call enemies[i].updateBullet(); when you move and show the enemies.
And when you want the enemies to shoot another shot, call enemies[i].resetBullet();
You can see my implementation here.

Can't animate multiple instances of an element with JavaScript. Only the first animation will be executed

I have created an element called Particle. Single animation of this element also works. But as soon as I try to run multiple animations, only one animation gets performed. I think the problem is the requestAnimationFrame (this.animate.bind(this))-call, but I don't know how to change it to accept multiple animations at once. Any ideas on how to fix this?
Code:
//gloabl vars
let particels = [];
let numberParticels = 120;
let canvas;
let ctx;
let title;
let mesaureTitle;
let boundRadius;
let animations;
window.onload = function () {
this.init();
for(let i = 0; i < numberParticels; i++){
particels[i].update();
particels[i].draw();
particels[i].animate(0);
}
}
function init(){
canvas = document.getElementById("c");
ctx = canvas.getContext("2d");
title = document.getElementById("title");
mesaureTitle = title.getBoundingClientRect();
bound = {
x: mesaureTitle.x,
y: mesaureTitle.y,
width: mesaureTitle.width,
height: mesaureTitle.height,
};
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
for(let i = 0; i < numberParticels; i++){
let x = Math.floor(Math.random() * window.innerWidth);
let y = Math.floor(Math.random() * window.innerHeight);
let size = Math.floor(Math.random() * 25) + 3;
let weight = Math.floor(Math.random() * 11) + 2;
particels.push(new Particel(x, y, size, weight));
}
}
class Particel {
constructor (x,y,size, weight) {
this.x = x;
this.y = y;
this.size = size;
this.directionX = 0.15332;
this.resetWeight = weight;
this.weight = weight;
this.lastTime = 0;
this.interval = 1000/60;
this.timer = 0;
}
update(){
this.weight += 0.02;
this.y = this.y + this.weight;
this.x += this.directionX;
//check for collision with textField
if (this.x < bound.x + bound.width
&& this.x + this.size > bound.x &&
this.y < bound.y + bound.height &&
this.y + this.size > bound.y) {
this.y -= 3;
this.weight *= -0.3;
}
}
draw(){
if(this.y > canvas.height){
this.y = 0 - this.size;
this.weight = this.resetWeight;
//create random start point
this.x = Math.floor(Math.random() * canvas.width);
}
ctx.fillStyle = "rgb(0, 180, 97)";
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
}
animate(timeStamp){
const deltaTime = timeStamp - this.lastTime;
this.lastTime = timeStamp;
if(this.timer > this.interval){
ctx.clearRect(0,0, canvas.width, canvas.height);
this.update();
this.draw();
this.timer = 0;
}else {
this.timer += deltaTime;
}
ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
requestAnimationFrame(this.animate.bind(this));
}
}
The major problem is clearing the canvas inside the animate method of each particle. So if you draw multiple particles, each particle update call clears the canvas, overwriting previous particle data, which only leaves the last particle visible.
You could try removing the
ctx.clearRect(0,0, canvas.width, canvas.height);
line from where it is and create a createAnimationFrame call back in init to clear the canvas before amimating particles:
function init() {
// ....
requestAnimationFrame( ()=> ctx.clearRect(0,0, canvas.width, canvas.height));
// existing for loop:
for(let i = 0; i < numberParticels; i++){
// ... .
}
However this creates (one plus the number of particles) requests for an animation frame. A better solution would be to remove requesting animation frames from the Particel class and create a single requestAnimationFrame callback which goes through the particels array and calls a class method to redraw each particle on the canvas with updated position.
Also the code generates an error in strict mode that bound has not been declared. I suggest declaring it globally rather than relying on sloppy mode JavaScript creating it as a window property for you.

How to constantly generate a moving shape with Javascript and Canvas

I'm currently developing a small game for my capstone project. In the game, the user tries to avoid rectangles of random sizes the move from the right side of the screen to the left at a set speed.
It's built using object-oriented Javascript, and I've assigned it an anonymous function, however, I can't seem to get it to generate a shape and animate it more than the initial time the function is called. The problem can be solved if I create more than one object, but I would like this function to run automatically and generate more than just the first rectangle.
I've tried to call the function with an interval to force it to re-run the function with no results. I also attempted to separate the initialization function to call it with a parameter to generate the number of shapes given to it.
This is the function that generates the shape with the initial call, and determines the color, size, and location as well as draws it on the canvas.
var randomRectangle = function(){
this.init = function() {
this.speed = 4;
this.x = canvas.width-50;
this.y = Math.floor(Math.random()*280) + 40;
this.w = Math.floor(Math.random()*200) + 50;
this.h = Math.floor(Math.random()*150) + 20;
this.col = "#b5e61d";
}
this.move = function(){
this.x -= this.speed;
}
this.draw = function(num){
draw.rectangles(this.x, this.y, this.w, this.h, this.col);
}
};
This is where the object is initialized and the loop generates all objects and animations on the canvas.
randRecs = new randomRectangle();
randRecs.init();
function loop(){
draw.clear();
player.draw();
player.move();
wall1.draw();
wall2.draw();
randRecs.draw();
randRecs.move();
}
var handle = setInterval(loop, 30);
I expected the rectangle to continuously be generated at a new y-coordinate with a new size, then move from the right side of the screen to the left. However, only one rectangle is created and animated.
var list = [];
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var randomRectangle = function() {
this.init = function() {
this.speed = 4;
this.x = canvas.width - 50;
this.y = Math.floor(Math.random() * 280) + 40;
this.w = Math.floor(Math.random() * 200) + 50;
this.h = Math.floor(Math.random() * 150) + 20;
this.col = "#b5e61d";
}
this.move = function() {
this.x -= this.speed;
// restart x position to reuse rectangles
// you can change the y value here to a new random value
// or you can just remove with array.splice
if (this.x < -50) this.x = canvas.width - 50;
}
this.draw = function(num) {
draw.rectangles(this.x, this.y, this.w, this.h, this.col);
}
};
function loop() {
draw.clear();
//player.draw();
//player.move();
//wall1.draw();
//wall2.draw();
// call the methods draw and move for each rectangle on the list
for (var i=0; i<list.length; i++) {
rec = list[i];
rec.draw();
rec.move();
}
}
// spawn any number of new rects in a specific interval
var rectsPerSpawn = 1;
function addRects() {
for (var i=0; i<rectsPerSpawn; i++) {
if (list.length < 100) {
var rec = new randomRectangle();
list.push(rec);
rec.init();
}
}
}
// every half second will spawn a new rect
var spawn = setInterval(addRects, 500);
var draw = {
clear: function () {
ctx.clearRect(0,0,canvas.width,canvas.height);
},
rectangles: function (x, y, w, h, col) {
ctx.fillStyle = col;
ctx.fillRect(x,y,w,h);
}
}
var handle = setInterval(loop, 30);
<canvas></canvas>

javascript and html canvas animation image emitter

Hello I have been watching some tutorials on HTML canvas and animations and I wanted to create my own. I am having trouble though. :/
I am trying to create some clouds that start on the left side of the screen and move to the right side of the screen, and will eventually disappear when they get to a certain point. I don't have that code yet. I don't know how to handle transparency. But, that is not where my troubles lie.
Currently, my clouds do not move. I can generate 20 different clouds in different locations but they are failing to move. I have checked my code with other tutorials and I can not seem to find why it's not working. Maybe because I am using an image?? If I could find some help I would really appreciate it. Thank you.
$(function(){
var leftcloudsrc = "ls/pics/cloud1.png";
var rightcloudsrc = "ls/pics/cloud2.png";
var canvas = document.getElementById('cloud');
var cw = canvas.width;
var ch = canvas.height;
var cloudsArray = [];
createclouds();
function createclouds(){
for (var i=0; i < 20; i++){
var x = Math.random() * 150;
var y = Math.random() * 300;
var v = Math.random() * 4;
cloudsArray.push(new Cloud(x, y, v));
}
animate();
console.log(cloudsArray);
}
function animate(){
requestAnimationFrame(animate);
for (var i = 0; i<cloudsArray.length; i++){
cloudsArray[i].move();
//new Cloud(x, y, v).create();
}
}
function Cloud(x, y, v){
this.x = x;
this.y = y;
this.v = v;
this.create = function(){
img = new Image,
ctx = document.getElementById('cloud').getContext('2d');
img.src = leftcloudsrc;
var iw = img.naturalWidth;
var ih = img.naturalHeight;
ctx.drawImage(img, x, y);
}
this.move = function(){
this.x += this.v;
this.create();
}
}
// var cloud = new Cloud(0,0,0);
// cloud.create();
});
i have tried writing to the console to make sure the information is saving and sticking, and yes, it is. i have even tried writing the .move() function to console to make sure the data changes, and it does. but it does not reflect visually???
ctx.drawImage(img, x, y); // wrong
ctx.drawImage(img, this.x, this.y); // right
You arent updating x and y. You are updating this.x and this.y
full code
// test
var leftcloudsrc = "http://www.freepngimg.com/download/cloud/10-cloud-png-image.png";
var rightcloudsrc = "ls/pics/cloud2.png";
var canvas = document.getElementById('cloud');
// you dont have to define ctx again and again
var ctx = canvas.getContext('2d');
var cw = canvas.width;
var ch = canvas.height;
var cloudsArray = [];
createclouds();
function createclouds() {
for (var i = 0; i < 20; i++) {
var x = Math.random() * 150;
var y = Math.random() * 300;
var v = Math.random() * 4;
cloudsArray.push(new Cloud(x, y, v));
}
animate();
console.log(cloudsArray);
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, cw, ch)
for (var i = 0; i < cloudsArray.length; i++) {
var c = cloudsArray[i]
c.move();
// remove when crosses the canvas width
if(c.x >= 500) {
cloudsArray.splice(i, 1);
}
//new Cloud(x, y, v).create();
}
}
function Cloud(x, y, v) {
this.x = x;
this.y = y;
this.v = v;
this.create = function() {
// invoke the constructor
var img = new Image;
img.src = leftcloudsrc;
var iw = img.naturalWidth;
var ih = img.naturalHeight;
ctx.drawImage(img, this.x, this.y);
}
this.move = function() {
this.x += this.v;
this.create();
}
}
<canvas id="cloud" width="500" height="400"></canvas>

How to multiply a function in a SetInterval on a addEventListener('click');

So I made this code (with the help of a tutorial) and I want to create the effect wherever I click.
The code does what it has to, but every time I do another click and call the 'drawParticle'-function, the process speeds up and less particles are been shown.
I think this is because I call on multiple setIntervals at once while JavaScript is single threaded. But how can I fix it?
var canvas,
c,
particles,
particleIndex,
particleNum,
w,
h;
window.onload =function(){
canvas = document.createElement("canvas");
c = canvas.getContext("2d");
particles = {};
particleIndex = 0;
particleNum = 3;
w = window.innerWidth;
h = window.innerHeight;
canvas.width = w;
canvas.height = h;
document.body.appendChild(canvas);
c.fillStyle ="black";
c.fillRect(0,0,canvas.width,canvas.height);
canvas.addEventListener('click', drawParticle, false);
};
function drawParticle(e){
function Particle(){
this.x = e.clientX;
this.y = e.clientY;
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
this.gravity = 0.2;
particleIndex++;
particles[particleIndex] = this;
this.id = particleIndex;
this.life = 0;
this.maxLife = Math.random() * 30 + 50;
this.color = "rgb("+parseInt(Math.random()*255,10)+",0,0)";
}
Particle.prototype.draw = function(){
this.x += this.vx;
this.y += this.vy;
if(Math.random() < 0.1 ){
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
}
//this.vy += this.gravity;
this.life++;
if(this.life >= this.maxLife){
delete particles[this.id];
}
c.fillStyle = this.color;
c.fillRect(this.x,this.y,10,10);
};
interval = setInterval(function(){
//c.globalCompositeOperation ="source-over";
c.fillStyle ="rgba(0,0,0,0.3)";
c.fillRect(0,0,canvas.width,canvas.height);
for (var i = 0; i < particleNum; i++){
new Particle();
}
//c.globalCompositeOperation ="lighter";
for (var i in particles){
particles[i].draw();
}
},30);
};
I think you're correct about the interval. I'm not sure if this is the reaction you want but if you clear the interval before redrawing, things don't speed up. Try something like:
var interval = null;
function drawParticle(e){
if (interval) {
clearInterval(interval)
}
function Particle(){
...
There's a codepen here http://codepen.io/bunnymatic/pen/BjwVBM
UPDATE:
I've changed the code pen to include the new code which is included below. The primary change was to make a Particles object which would hold the set of particles being drawn for each click. By moving this collection of particles and their particleIndex seems to have solved the issue. I suspect there was something strange about adding each element to the global particles list. Now the Particles instance manages just the particles associated with that group.
I also took out particleId because it seemed unnecessary, though you may need to put it back in if it's useful for other stuff out side of this code.
Here's my updated code:
var canvas,
c,
particleNum,
w,
h;
window.onload =function(){
canvas = document.createElement("canvas");
c = canvas.getContext("2d");
particleNum = 3;
w = window.innerWidth;
h = window.innerHeight;
canvas.width = w;
canvas.height = h;
document.body.appendChild(canvas);
c.fillStyle ="black";
c.fillRect(0,0,canvas.width,canvas.height);
canvas.addEventListener('click', drawParticles, false);
};
function Particles() {
this.particles = {};
this.particleIndex = 0;
}
Particles.prototype.addParticle = function(particle) {
this.particles[++this.particleIndex] = particle;
}
Particles.prototype.killDeadParticles = function() {
for (var i in this.particles) {
var particle = this.particles[i];
if (particle.isDead()) {
delete this.particles[i]
}
}
}
Particles.prototype.draw = function() {
// draw particles
for (var i in this.particles){
this.particles[i].draw();
}
this.killDeadParticles();
}
function Particle(e){
this.x = e.clientX;
this.y = e.clientY;
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
this.gravity = 0.2;
this.life = 0;
this.maxLife = Math.random() * 30 + 50;
this.color = "rgb("+parseInt(Math.random()*255,10)+",0,0)";
// console.log('new particle', particleIndex);
}
Particle.prototype.isDead = function() {
return (this.life >= this.maxLife);
}
Particle.prototype.draw = function(){
this.x += this.vx;
this.y += this.vy;
if(Math.random() < 0.1 ){
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
}
//this.vy += this.gravity;
this.life++;
c.fillStyle = this.color;
c.fillRect(this.x,this.y,10,10);
};
function drawParticles(e){
var particles = new Particles(e);
setInterval(function(){
//c.globalCompositeOperation ="source-over";
c.fillStyle ="rgba(0,0,0,0.3)";
c.fillRect(0,0,canvas.width,canvas.height);
for (var i = 0; i < particleNum; i++){
var particle = new Particle(e);
particles.addParticle(particle);
}
//c.globalCompositeOperation ="lighter";
particles.draw();
},30);
};

Categories