How to update text written on canvas - javascript

When I try to change the scoreboard using the computerCount variable (as you can see in my if statement in the update function for the ball variable) the other numbers aren't shown properly. Keep in mind that at first 0 works properly.
JSFiddle format - You can change code by going at top right corner that says "Edit" in JSFiddle.
Code:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Pong</title>
<!-- Basic styling, centering the canvas -->
<style>
canvas{
position: absolute;
margin: auto;
top:60px;
bottom:0;
right:0;
left:0;
}
#scoreBoard{
position: absolute;
margin: auto;
top:0;
bottom:640px;
right:720px;
left:20px;
}
</style>
</head>
<body>
<canvas id = "scoreBoard" width="500" height = "100"></canvas>
<script>
var WIDTH = 700
var HEIGHT = 600
var pi = Math.PI
var canvas
var ctx
var keystate
var upArrow = 38;
var downArrow = 40;
var computerCount = 0;
var playerCount = 0;
var player = {
x: null,
y: null,
width: 20,
height: 100,
/**
* Update the position depending on pressed keys
*/
update: function() {
if(keystate[upArrow]){
this.y-=7;
}
if(keystate[downArrow]){
this.y+=7;
}},
/**
* Draw the player paddle to the canvas
*/
draw: function() {
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
/**
* The ai paddle
*
* #type {Object}
*/
var ai = {
x: null,
y: null,
width: 20,
height: 100,
/**
* Update the position depending on the ball position
*/
update: function() {
var desty = ball.y-(this.height - ball.side)*0.5
this.y += (desty-this.y)*0.2;
},
draw: function() {
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
/**
* The ball object
*
* #type {Object}
*/
var ball = {
x: null,
y: null,
vel:null,
side: 20,
speed:5,
update: function() {
this.x += this.vel.x;
this.y += this.vel.y;
if(0>this.y || this.y+this.side>HEIGHT){
var offset = this.vel.y<0 ? 0-this.y : HEIGHT-(this.y+this.side);
this.y+=2*offset
this.vel.y*=-1;
}
var AABBIntersect = function(ax, ay, aw, ah, bx, by, bw, bh) {
return ax < bx+bw && ay < by+bh && bx < ax+aw && by < ay+ah;
};
var pdle = this.vel.x<0 ? player : ai;
if(AABBIntersect(pdle.x,pdle.y,pdle.width,pdle.height, this.x, this.y, this.side, this.side)){
this.x = pdle===player ? player.x + player.width : ai.x - this.side;
var n = (this.y+this.side-pdle.y)/(pdle.height+this.side)
var phi = 0.25*pi*(2*n - 1) // pi/4 = 45
this.x = pdle===player ? player.x+player.width : ai.x - this.side;
var n = (this.y+this.side - pdle.y)/(pdle.height+this.side);
var phi = 0.25*pi*(2*n - 1); // pi/4 = 45
this.vel.x = (pdle===player ? 1 : -1)*this.speed*Math.cos(phi);
this.vel.y = this.speed*Math.sin(phi);
}
if(ball.x<0){
computerCount =+1;
ball.x = (WIDTH - ball.side) / 2;
ball.y = (HEIGHT - ball.side) / 2;
}
},
// reset the ball when ball outside of the canvas in the
draw: function() {
ctx.fillRect(this.x, this.y, this.side, this.side);
}
}
function score(){
}
/**
* Starts the game
*/
function main() {
// create, initiate and append game canvas
canvas = document.createElement("canvas");
canvas.width = WIDTH;
canvas.height = HEIGHT;
ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
canvas2 = document.createElement("canvas");
canvas2.setAttribute("id", "scoreBoard");
canvas2.width = 200;
canvas2.height = 100;
ctx2 = canvas2.getContext("2d");
document.body.appendChild(canvas2);
keystate = {};
document.addEventListener("keydown" , function(event){
keystate[event.keyCode] = true;
})
document.addEventListener("keyup" , function(event){
delete keystate[event.keyCode];
})
init();
var loop = function() {
update();
draw();
window.requestAnimationFrame(loop, canvas);
};
window.requestAnimationFrame(loop, canvas);
}
/**
* Initatite game objects and set start positions
*/
function init() {
player.x = player.width;
player.y = (HEIGHT - player.height) / 2;
ai.x = WIDTH - (player.width + ai.width);
ai.y = (HEIGHT - ai.height) / 2;
ball.x = (WIDTH - ball.side) / 2;
ball.y = (HEIGHT - ball.side) / 2;
ball.vel = {
x:ball.speed,
y: 0
}
}
/**
* Update all game objects
*/
function update() {
ball.update();
player.update();
ai.update();
}
function draw(){
ctx.fillStyle = "black"
ctx.fillRect(0,0,WIDTH,HEIGHT)
ctx.save();
ctx2.fillStyle = "red"
ctx2.fillText(String(computerCount),100,100)
ctx2.fillText("VS",120,100)
ctx2.fillText(String(playerCount),175,100)
ctx2.font = "bold 40px monaco"
ctx.fillStyle = "white"
ball.draw();
player.draw();
ai.draw();
var w=4;
var x= (WIDTH-w)*0.5;
var y=2;
var step = HEIGHT/20;
while(y<HEIGHT){
ctx.fillRect(x,y+step*0.25,w,step*0.5)
y+=step;
}
ctx.restore();
}
// start and run the game
main();
</script>
</body>
</html>

You have two problems:
When you draw a number, you do not erase the previous number that is already printed. That is, when the score becomes 1, you render 1 on top of already rendered 0. The easiest way to address it is to do fillRect and draw a black rectangle on top of the previously written score before you draw a new score.
When you fix that, you will notice that your score never goes above 1. The reason is a typo in the code that increments it, replace
computerCount =+1;
with
computerCount += 1;

Mentioned by #Ishamael, line 139's computer count needs to be changed to computerCount += 1; //After this if statement add if the player scores.
In line 221,
function draw(){
ctx.fillStyle = "black";
ctx.fillRect(0,0,WIDTH,HEIGHT);
ctx.save();
ctx2.fillStyle = "black";
ctx2.fillRect(0,0,WIDTH, 100);
ctx2.save();
ctx2.fillStyle = "red";
ctx2.font = "bold 40px monaco";
ctx2.fillText(String(computerCount),100,100);
ctx2.fillText("VS",120,100);
ctx2.fillText(String(playerCount),175,100);
ctx2.save();
ctx.fillStyle = "white";
. . .
}
remember your semicolons and set text style before writing. I also added the repaint to erase the previous scoreboard.

Related

Javascript Canvas Flashing When Drawing Frames

Been having issues with this for a couple days, not sure why the text I'm rendering on the canvas is flashing so much. I'm using the requestAnimationFrame() function, but its still flickering.
What I want to have happen is for the text to move smoothly and they remove themselves from the array when they move completely off screen.
var canvas = document.getElementById("myCanvas");
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var w = canvas.width;
var h = canvas.height;
var texts = [];
window.addEventListener("load", init);
function init() {
draw();
mainLoop();
}
function mainLoop() {
texts.push(createText("wow", "blue"));
texts.push(createText("wow", "green"));
texts.push(createText("wow", "red"));
setTimeout(function() { mainLoop(); }, 100);
}
function Text(x, y, vx, vy, varText, theColor){
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.color = theColor;
this.draw = function() {
drawStroked(varText, this.x, this.y, this.color);
}
}
function drawStroked(text, x, y, color) {
c.font = "30px bold Comic Sans MS";
c.strokeStyle = 'black';
c.lineWidth = 8;
c.strokeText(text, x, y);
c.fillStyle = color;
c.fillText(text, x, y);
}
function createText(varText, color) {
var x = (Math.random() * w / 2 ) + w/4;
var y = (Math.random() * h / 2) + h/2;
var vx = (Math.random() * .5) - .25
var vy = -(Math.random() * 3) - 1
return new Text(x, y, vx, vy, varText, color);
}
function draw() {
c.clearRect(0, 0, c.canvas.width, c.canvas.height);
for(var i = 0;i < texts.length; i++) {
var currentText = texts[i];
currentText.x += currentText.vx;
currentText.y += currentText.vy;
currentText.draw();
if(currentText.x>w||currentText.x<0||currentText.y<10){
texts.splice(i, 1);
}
}
requestAnimationFrame(draw);
}
body {
margin: 0;
padding: 0;
overflow: hidden;
}
<!DOCTYPE html>
<html>
<head>
<title>Game Screen</title>
</head>
<body>
<canvas id="myCanvas"></canvas>
</body>
</html>
The flickering you are seeing results from your looping code, where you skip elements of the array when deleting elements (Element 3 needs to be deleted? You call splice(3, 1) and then continue with the loop at index 4. Since the array shifts when you call splice, you should process element 3 again).
The imho easiest way to fix this is to iterate backwards over the array. Please note that iterating backwards is less CPU cache efficient (because every array access leads to a cache miss), so another fix would be
if(currentText.x>w||currentText.x<0||currentText.y<10){
texts.splice(i--, 1); // decrement i after accessing and deleting
}
var canvas = document.getElementById("myCanvas");
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var w = canvas.width;
var h = canvas.height;
var texts = [];
window.addEventListener("load", init);
function init() {
draw();
mainLoop();
}
function mainLoop() {
texts.push(createText("wow", "blue"));
texts.push(createText("wow", "green"));
texts.push(createText("wow", "red"));
setTimeout(function() { mainLoop(); }, 100);
}
function Text(x, y, vx, vy, varText, theColor){
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.color = theColor;
this.draw = function() {
drawStroked(varText, this.x, this.y, this.color);
}
}
function drawStroked(text, x, y, color) {
c.font = "30px bold Comic Sans MS";
c.strokeStyle = 'black';
c.lineWidth = 8;
c.strokeText(text, x, y);
c.fillStyle = color;
c.fillText(text, x, y);
}
function createText(varText, color) {
var x = (Math.random() * w / 2 ) + w/4;
var y = (Math.random() * h / 2) + h/2;
var vx = (Math.random() * .5) - .25
var vy = -(Math.random() * 3) - 1
return new Text(x, y, vx, vy, varText, color);
}
function draw() {
c.clearRect(0, 0, c.canvas.width, c.canvas.height);
for(var i = texts.length - 1;i >= 0; i--) {
var currentText = texts[i];
currentText.x += currentText.vx;
currentText.y += currentText.vy;
currentText.draw();
if(currentText.x>w||currentText.x<0||currentText.y<10){
texts.splice(i, 1);
}
}
requestAnimationFrame(draw);
}
body {
margin: 0;
padding: 0;
overflow: hidden;
}
<!DOCTYPE html>
<html>
<head>
<title>Game Screen</title>
</head>
<body>
<canvas id="myCanvas"></canvas>
</body>
</html>

Initializing a possibly infinite array?

I'm attempting to make sand fall from the mouse (i've changed the x and y of the sand to be the center of the canvas for testing purposes.)
I'm having some issues with one of my functions. drawSand() will not run - I believe it's because sandObj[] can't be initialized before hand, but i'm uncertain. i've tried initializing it by just limiting the array to 200 elements but that didn't seem to help any. I've added some booleans to test to see if the function is finishing and draw is the only one that isn't (besides the drawPaintTool, it's not enabled on purpose) Any help / criticism would be helpful!
"use strict";
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var ballX = 0;
var ballY = 0;
var ballRadius = 20;
var sandObj = [];
var sandActive = false;
var sandAmount = 10;
var sandX = 10;
var sandY = 0;
var testDrawPaintToolFunction = false;
var testPaintToolFunction = false;
var testDrawSandFunction = false;
var testMoveSandFunction = false;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
sandAmountDisplay();
paintTool();
moveSand();
drawSand();
}
function drawPaintTool() {
ctx.beginPath();
ctx.arc(ballX, ballY, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "white";
ctx.fill();
ctx.closePath();
testDrawPaintToolFunction = true;
}
function paintTool() {
if (sandActive == true) {
sandAmount++;
}
testPaintToolFunction = true;
}
function drawSand() {
for (var i = 0; i < sandAmount; i++) {
sandObj = {
x: canvas.width / 2,
y: canvas.height / 2
};
ctx.beginPath();
ctx.rect(sandObj[i].x, sandObj[i].y, 5, 5);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
}
testDrawSandFunction = true;
}
function moveSand() {
testMoveSandFunction = true;
}
function sandAmountDisplay() {
ctx.font = "16px Arial";
ctx.fillStyle = "black";
ctx.fillText("Sand Amount: " + sandAmount, 8, 20);
ctx.fillText("Sand Active: " + sandActive, 8, 40);
ctx.fillText("drawPaintTool: " + testDrawPaintToolFunction, 8, 60);
ctx.fillText("PaintTool: " + testPaintToolFunction, 8, 80);
ctx.fillText("drawSand: " + testDrawSandFunction, 8, 100);
ctx.fillText("moveSand: " + testMoveSandFunction, 8, 120);
}
document.addEventListener("mousemove", mouseMoveHandler, false);
document.addEventListener("mousedown", mouseDownHandler);
document.addEventListener("mouseup", mouseUpHandler);
function mouseMoveHandler(e) {
var relativeX = e.clientX - canvas.offsetLeft + ballRadius / 2;
var relativeY = e.clientY - canvas.offsetTop + ballRadius / 2;
if (relativeX > 0 && relativeX < canvas.width) {
ballX = relativeX - ballRadius / 2;
}
if (relativeY > 0 && relativeY < canvas.height) {
ballY = relativeY - ballRadius / 2;
}
}
function mouseDownHandler() {
sandActive = true;
}
function mouseUpHandler() {
sandActive = false;
}
setInterval(draw, 10);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Gamedev Canvas Workshop</title>
<style>
* {
padding: 0;
margin: 15;
}
canvas {
background: #eee;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="480" height="320"></canvas>
<script src="sand.js">
</script>
</body>
</html>
so to just help out some, because if I comment the code I would most likely have to write a larger paragraph just to get this posted, i'm going to explain the drawSand function. So the for loop used is for re-drawing all the sand everytime it's called - however, i've had to set the x and y here because I couldn't think of a way to initialize something that could continuously spawn sand. I'm lost to be honest.
EDIT: Also the sandAmount is changing constantly which is most likely the problem with something like this. when the mouse is held down the sandAmount goes up - which is what paintTool is for.
Inside of this function:
function drawSand() {
for (var i = 0; i < sandAmount; i++) {
sandObj = {
x: canvas.width / 2,
y: canvas.height / 2
};
ctx.beginPath();
ctx.rect(sandObj[i].x, sandObj[i].y, 5, 5);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
}
testDrawSandFunction = true;
}
...you are assigning a new object value to sandObj, which was previously an array. You are accessing it as an array with sandObj[i]... further down, so I'm guessing that was a mistake on your part. Perhaps you meant to do:
sandObj[i] = {
x: canvas.width / 2,
y: canvas.height / 2
};
...rather than:
sandObj = {
x: canvas.width / 2,
y: canvas.height / 2
};

Create bullets for a game at an interval

I have a game, and in it there is a main, controllable character and then enemies that shoot back at the character. For the enemies, when they shoot back I want them to shoot at intervals so that it isn't just one massive block of bullets, and it worked with a setInterval for one, but when a second enemy comes in they don't shoot. Only one of the two will. If anybody has a solution that would be great!
function enemies() {
if (enemy_soldiers.length == 0) {
level += 0.2;
for (var i = 0; i<(1 + Math.floor(Math.round(level))); i++) {
var gx = 1450
var gy = getRandom(430, 630);
enemy_soldiers.push({
x: gx,
y: gy,
l: gl,
d: getRandom(350, 600),
shooting: false,
interval: setInterval (function() {enemy.shooting = true;},fire_rate),
shoot: function() {
enemy_bullets.push({
x: enemy.x+40,
y: enemy.y+87,
vel: 10,
});
}
});
}
}
var enemy;
gctx.clearRect(0, 0, 1400, 800);
for (var i in enemy_soldiers) {
enemy = enemy_soldiers[i];
drawenemy(enemy.x, enemy.y, enemy.l);
//ai
if (distance(enemy.x, enemy.y, cx, cy) >= enemy.d && enemy.x>cx) {
enemy.x-=vel;
}
else if (distance(enemy.x, enemy.y, cx, cy) >= enemy.d && enemy.x<cx) {
enemy.x+=vel;
}
if (distance(enemy.x, enemy.y, cx, cy) <= 600) {
if (enemy.shooting == true) {
enemy.shoot(enemy.x,enemy.y);
enemy.shooting = false;
}
gbctx.clearRect(0, 0, 1400, 800);
for (var j in enemy_bullets) {
enemy_bullet = enemy_bullets[j];
enemy_bullet.x -= enemy_bullet.vel;
if (enemy_bullet.x > 1400 || enemy_bullet.x < -5 || enemy_bullet.y > 800 || enemy_bullet.y < -5) {
enemy_bullets.splice(j,1);
}
drawEnemyBullet(enemy_bullet.x, enemy_bullet.y);
}
}
}}
This solution relies on the function Date.now() which returns the current time in milliseconds. It's possible for each enemy to keep track of when they have to fire next and each one can have a different delay between shots.
I've only added a graphic to show when they are firing their weapons, but this can be easily altered to something like a raycast or spawn a projectile particle.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
position: absolute;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
border: solid 1px white;
border-radius: 1px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
// Enemy constructor
function Enemy(x,y) {
this.x = x;
this.y = y;
this.isFiring = false;
this.fireDelay = (500 + Math.random() * 500) | 0; // Time between shots
this.lastFired = Date.now(); // Last time at which the enemy fired
}
/*
Enemy prototype
(All objects created using the constructor share these properties)
E.G.
var e1 = new Enemy(10,0);
var e2 = new Enemy(20,0);
if (e1.__proto__ === e2.__proto__) {
console.log("Match");
}
prints "Match"
*/
Enemy.prototype = {
WIDTH: 10,
HEIGHT: 20,
FIRE_DURATION: 100, // Amount of time 'fire' graphic is shown
FIRE_WIDTH: 10,
FIRE_HEIGHT: 10,
update: function() {
// If current time - time when I last fired > the amount of time between shots
if (Date.now() - this.lastFired > this.fireDelay) {
this.lastFired = Date.now();
this.isFiring = true;
// If you were using projectile particles, this is where you would spawn one
}
if (this.isFiring && Date.now() - this.lastFired > this.FIRE_DURATION) {
this.isFiring = false;
}
},
render: function(ctx) {
ctx.fillStyle = "darkred";
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.rect(
this.x,
this.y,
this.WIDTH,
this.HEIGHT
);
ctx.fill();
ctx.stroke();
if (this.isFiring) {
ctx.fillStyle = "yellow";
ctx.strokeStyle = "darkyellow";
ctx.lineWidth = 3;
ctx.beginPath();
ctx.rect(
this.x + this.WIDTH,
this.y + this.HEIGHT * 0.5 - this.FIRE_HEIGHT * 0.5,
this.FIRE_WIDTH,
this.FIRE_HEIGHT
);
ctx.fill();
ctx.stroke();
}
}
};
var canvasWidth = 180;
var canvasHeight = 160;
var canvas = null;
var ctx = null;
var enemies = [];
window.onload = function() {
canvas = document.getElementById("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
ctx = canvas.getContext("2d");
for (var i = 0; i < 5; ++i) {
enemies[i] = new Enemy(20 + i * 3,10 + i * 30);
}
loop();
}
function loop() {
// Update
for (var i = 0; i < enemies.length; ++i) {
enemies[i].update();
}
// Render
ctx.fillStyle = "gray";
ctx.fillRect(0,0,canvasWidth,canvasHeight);
for (var i = 0; i < enemies.length; ++i) {
enemies[i].render(ctx);
}
//
requestAnimationFrame(loop);
}
</script>
</body>
</html>

HTML 5 Canvas - making ball bounce on click & c.fill doesn't work?

I'm trying to make a ball which moves on a line and it bounces on it when clicked on it, but I don't know how to make it bounce on click.
Also, c.fill() doesn't work, I don't know why. (I've used it to fill the ball, maybe it doesn't work like that?)
Any advice would be really helpful since I'm a beginner in canvas, but know these 2 problems really concern me as I can't find any solutions for any of them. 😞
document.addEventListener('DOMContentLoaded', function () {
var canvas = document.querySelector('canvas');
var canvasx = document.getElementById('hr');
var c = canvas.getContext('2d');
canvas.width = canvasx.clientWidth;
canvas.height = canvasx.clientHeight;
var x = 80;
var y = 30;
var dx = 6;
var dy = 2;
var radius = 15;
var gravity = Math.random();
var friction = 0.9;
function animate() {
requestAnimationFrame(animate);
c.clearRect(0, 0, canvas.width, canvas.height);
c.beginPath();
c.arc(x, y, radius, 0, Math.PI * 2, false);
c.strokeStyle = "#eeede7";
c.stroke();
c.beginPath();
c.moveTo(50, canvas.height);
c.lineTo(1870, canvas.height);
c.lineWidth = 3;
c.fillStyle = "#77dff1";
c.fill();
c.strokeStyle = "#77dff1";
c.stroke();
update = function () {
if (y + 20 > canvas.height) {
dy = -dy * friction;
}
else {
dy += gravity;
}
y += dy;
x += dx;
}
function bounce() {
if ((x + radius + 50) > canvas.width || (x - radius - 50) < 0) {
dx = -dx;
}
}
update();
bounce();
}
animate();
function over() {
var px = event.pageX;
if (x - px < 0 || x - px > 0)
{
dx = -dx;
}
}
function bounceBall()
{
}
canvas.addEventListener('mouseover', over, false);
canvas.addEventListener('click', bounceBall, false)
}, false);
#hr {
position: relative;
bottom: 5%;
width: 100%;
margin: 0 auto;
}
hr {
border-color: #77dff1;
max-width: 90%;
}
canvas {
width: 100%;
}
<div id="hr">
<canvas></canvas>
<script src="js/canvas.js"></script>
</div>

how to fix the bliking of the balls in the canvas?

An object type of ball is created with a new constructor function ball(color).
Its properties include all that need to paint it on a canvas and to cause him to move in a random direction.
When one ball is created orangeBall = new ball('orange'), it shows up well including its movement on the canvas. But when another one is added, both of them start to blink.
How to solv it ?
Thanks.
<!DOCTYPE html>
<html lang="he">
<head>
<meta charset="utf-8" />
<title>
כדורים קופצים רנדומלית בצבעים שונים
</title>
<style>
html,body {
margin: 0;
padding: 0;
background: black;
}
.container { width:900px; margin:0 auto; }
#canvas { background:#8613eb; border:1px solid #cbcbcb; }
</style>
<script>
var ctx;
var H = 800;
var W = 800;
window.onload = function () {
ctx = canvas.getContext("2d");
canvas.width=W;
canvas.height=H;
function ball(color) {
//life - the amount of time to show the ball in the screen
this.life = 60*1000, //1 minute
this.color = arguments.length==1 ? color: 'white';
this.x= Math.round(Math.random()*W);
this.y= Math.round(Math.random()*H);
this.radius= 10 + Math.round(Math.random()*50);// between 10-60
this.dx=1+ Math.round(Math.random()*5); //between 1-6
this.dy= 2+ Math.round(Math.random()*4); //between 2-6
this.startAngel= 0;
this.endAngel= 2*Math.PI; //360deg
this.speed= 3+Math.round(Math.random()*50) //3-50msec
this.show = function() {
//first clear the previous ball
ctx.clearRect(0,0,canvas.width,canvas.height);
var xClear = (this.x-this.radius) <=0 ? 0:(this.x - this.radius);
var yClear = (this.y-2*this.radius) <=0 ? 0:(this.y - 2*this.radius);
ctx.clearRect(xClear,yClear,canvas.width,canvas.height);
//lets stroke the ball
ctx.beginPath();
ctx.fillStyle = this.color;
this.x+=this.dx;
this.y+=this.dy;
if (this.x<0 || this.x>W) {
this.dx=-this.dx;
}
if (this.y<0 || this.y>H) {
this.dy=-this.dy;
}
ctx.arc(this.x,this.y,this.radius,this.startAngel,this.endAngel);
ctx.closePath();
ctx.fill();
this.life-=this.speed;
var _this = this;
// creating new property in the ball 'timePointer'
this.timePointer = this.life<=0 ?
clearInterval(this.timePointer):
setTimeout(function() {
_this.show();
},this.speed);
}
this.show();
};
orangeBall = new ball('orange');
blackBall = new ball('black');
// whiteBall = new ball('white');
// yellowgeBall = new ball('yellow');
// pinkBall = new ball('pink');
// blueBall = new ball('blue');
// greenBall = new ball('green');
};
</script>
</head>
<body>
<div class="container">
<canvas id="canvas">Your browser doesn't support this game.</canvas>
</div>
</body>
</html>
You are clearing your whole canvas after every ball draws itself with its show function. Instead of having the balls clear the canvas, have an interval that will call a function that will clear the canvas once and then iterates all the balls and draw them.
Animations are divided up into frames. A frame is approx 1/60th of a second and in that time all of the animation is drawn.
To help with animations the browser has a function that you use to call the function that renders your frame. requestAnimationFrame(yourfunction)
requestAnimationFrame will tell the browser that you are making changes to an animation. This stops the canvas being presented to the display until the next vertical display refresh.
Using setInterval or setTimeout
function animateSomething(){
// draw something
} // as soon as this exits the content of the canvas is moved to the display
// there is no way to know where the display hardware is writing pixels to
// display, could be halfway through drawing a ball
setInterval(animateSomething,1000 / 60);
If you do this for many objects each time you exit the pixels are move to the display without regard to what the display hardware is doing. This causes flicker, shearing and other problems.
Using requestAnimationFrame
function animateSomething(){
// draw something
requestAnimationFrame(animateSomething)
} // The content of the canvas does not move to the display until the
// display hardware is getting ready to scan the next display refresh
requestAnimationFrame(animateSomething);
The best way to handle animations is to do all the rendering from one function.
Below i have modified your code to remove the flicker using requestAnimationFrame I have added a mainLoop function that draws all the balls. I let the browser handle the timing.
var ctx;
var H = 800;
var W = 800;
window.onload = function() {
ctx = canvas.getContext("2d");
canvas.width = W;
canvas.height = H;
function ball(color) {
//life - the amount of time to show the ball in the screen
this.life = 60 * 1000; //1 minute
this.color = arguments.length == 1 ? color : 'white';
this.x = Math.round(Math.random() * W);
this.y = Math.round(Math.random() * H);
this.radius = 10 + Math.round(Math.random() * 50); // between 10-60
this.dx = 1 + Math.round(Math.random() * 5); //between 1-6
this.dy = 2 + Math.round(Math.random() * 4); //between 2-6
this.startAngel = 0;
this.endAngel = 2 * Math.PI; //360deg
this.speed = 3 + Math.round(Math.random() * 50) //3-50msec
this.show = function() {
//first clear the previous ball
ctx.beginPath();
ctx.fillStyle = this.color;
this.x += this.dx;
this.y += this.dy;
if (this.x < 0 || this.x > W) {
this.dx = -this.dx;
}
if (this.y < 0 || this.y > H) {
this.dy = -this.dy;
}
ctx.arc(this.x, this.y, this.radius, this.startAngel, this.endAngel);
ctx.closePath();
ctx.fill();
this.life -= this.speed;
}
};
orangeBall = new ball('orange');
blackBall = new ball('black');
whiteBall = new ball('white');
yellowgeBall = new ball('yellow');
pinkBall = new ball('pink');
blueBall = new ball('blue');
greenBall = new ball('green');
var balls = [orangeBall, blackBall, whiteBall, yellowgeBall, pinkBall, blueBall, greenBall];
function mainLoop(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(var i = 0; i < balls.length; i++){
if(balls[i].life > 0){
balls[i].show();
}
}
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
};
html,
body {
margin: 0;
padding: 0;
background: black;
}
.container {
width: 900px;
margin: 0 auto;
}
#canvas {
background: #8613eb;
border: 1px solid #cbcbcb;
}
<div class="container">
<canvas id="canvas">Your browser doesn't support this game.</canvas>
</div>
I changed the code to use prototype. Is that better ?
In addition it's not smooth enough.
Added also performance timeline for your help.
var ctx;
var H = window.innerHeight;
var W = window.innerWidth;
window.onload = function () {
ctx = canvas.getContext("2d");
canvas.width=W;
canvas.height=H;
function ball(color) {
//life - the amount of time to show the ball in the screen
this.life = 60*10, //duration of 600 X 16ms(fram per sec)
this.color = arguments.length==1 ? color: 'white';
this.x= Math.round(Math.random()*W);
this.y= Math.round(Math.random()*H);
this.radius= 10 + Math.round(Math.random()*50);// between 10-60
this.dx=1+ Math.round(Math.random()*5); //between 1-6
this.dy= 2+ Math.round(Math.random()*4); //between 2-6
this.startAngel= 0;
this.endAngel= 2*Math.PI; //360deg
this.start = null;
};
ball.prototype.show = function() {
//lets stroke the ball
ctx.beginPath();
ctx.fillStyle = this.color;
this.x+=this.dx;
this.y+=this.dy;
if (this.x<0 || this.x>W) {
this.dx=-this.dx;
}
if (this.y<0 || this.y>H) {
this.dy=-this.dy;
}
ctx.arc(this.x,this.y,this.radius,this.startAngel,this.endAngel);
ctx.closePath();
ctx.fill();
this.life--;
};
var arrBalls = [] ;
arrBalls.push(new ball('orange'));
arrBalls.push(new ball('black'));
arrBalls.push(new ball('white'));
arrBalls.push(new ball('pink'));
arrBalls.push(new ball('blue'));
arrBalls.push(new ball('green'));
// This loop shell be less then 10ms inorder not to maintain
// 60fps interval of screen hardware rendering
var balldeth = 0;
function paint() {
//clear the canvas once in xframes
ctx.clearRect(0,0,canvas.width,canvas.height);
//paint balls in the canvas
for (var i = 0; i < arrBalls.length; i++ ) {
arrBalls[i].life >= 0 ? arrBalls[i].show() : ++balldeth;
}
(balldeth == arrBalls.length) ? console.log("game over"):window.requestAnimationFrame(paint);
}
window.requestAnimationFrame(paint);
};
html,body {
margin: 0;
padding: 0;
background: black;
}
#canvas { background:#8613eb; border:1px solid #cbcbcb; }
<body>
<canvas id="canvas">Your browser doesn't support this game.</canvas>
</body>

Categories

Resources