Javascript setInterval works but not infinite loop - javascript

I don't understand why the infinite loop does not work but uncommenting the function call works.
<body>
<canvas id="myCanvas"style="border:2px solid black;" width="1600" height="900">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
var c = document.getElementById ("myCanvas").getContext ("2d");
var boxSize = 25;
var numBoxes = 1;
var x = 1000;
var dx = 1;
var y = 100;
var dy = 1;
var a = 0.01;
var da = 0.1;
var size = 20;
var w = c.canvas.width;
var h = c.canvas.height;
//function testing () {
while (true) {
c.clearRect (0, 0, w, h);
x = x + 1;
y = y + 1;
a = a + 0.1;
x = x + dx;
y = y + dy;
if (x < size / 2 || x + (size / 2) > w) {
dx = -dx;
da = -da;
}
if (y < size / 2 || y + (size / 2) > h) {
dy = -dy;
da = -da;
}
c.save ();
c.translate (x, y);
c.rotate (a);
c.strokeRect (-size / 2, -size / 2, size, size);
c.restore ();
}
// testing ();
// setInterval (testing, 10);
</script>
</body>

When you use setInterval you keep adding the function you are calling to the queue of things for the browser to do. Repaint events are also added to this queue.
When you use an infinite loop, the browser never gets to the end of the function, so it never gets around to running a repaint and the image in the document never updates.

JavaScript-in-a-browser is a part of a larger construction. You need to align to the rules of this construction and don't hurt it. One of the rule is that your JavaScript must run quick and exit then. Exit means that control gets back to the framework, which does all the job, repaint the screen etc.
Try to hide and show something many times:
for (n = 0; n < 200; n++) {
$("#test").hide();
$("#test").show();
}
When this code runs, you won't see any flickering, you will see that the last command will have effect.
Have to say, it's not easy to organize code that way, if you want to make a cycle which paints nice anims on a canvas, you have to do it without while.

Related

Natural Movement with Noise

Im creating an object that randomly moves in a natural way using noise like this (works as intended):
The objects encounter a collision and their trajectory is manipulated, the movement path now changes to straight line (words as intended)
thisRabbit.x = _world.width * (noise(thisRabbit.t));
thisRabbit.y = _world.height * (noise(thisRabbit.t+5));
thisRabbit.t += 0.001;
The problem is after this movement , i want the object to start moving in a random direction again as it was initially. If i use the same function, the object jumps to the last location before the trajectory was modified.
let vx = this.acquiredFood[0] - this.x;
let vy = this.acquiredFood[1] - this.y;
let f = (this.genes.speed + 10) / Math.sqrt(vx*vx+vy*vy);
vx = vx * f;
vy = vy * f;
let newX = this.x + vx;
let newY = this.y + vy;
So how do i get the object to move as before, given a starting position
edit: snippet here: https://editor.p5js.org/vince.chinner/sketches/HPFKR8eIw
Your problem is that you used a factor from 0 to 1 generated with noise and an incremented seed to generate the position by multiplying directly the world dimentions. When reaching food, you cannot increment the seed as to be in the exact position where the movement to get your food led you (I found no inverse function for noise to get the seed from the return value).
What you need to do instead is use the noise to increment or decrement the coordinates, so that no matter where the seed is, you don't loose your current position.
Here are the different corrections I applied to the code, as there were also syntax errors, I can't really paste the whole stuff here for copyright reasons (you didn't share the whole code here and the sketch belongs to you)
MAIN CORRECTION:
used a var found because returning from the forEach callback doesn't make you leave the findFood function, but the callback one. And the forEach loop doesn't stop. Using this var prevents the further forEach tests to be made and allows you to return from findFood so that no further move is made after seeing food.
noise is now applied to a value of 4 and I subtract 2, so that x and y now change with a range of -2 to 2 each. Of course, with this method, you need to check against world dimentions or else the rabbit could leave the world. The seed increment has been changed too or else it would vary too slowly (adapt values as you wish)
findFood(){
var thisRabbit = this, found = false;
_world.food.forEach(f => {
if(!found){
let d = int(dist(f[0], f[1], thisRabbit.x, thisRabbit.y));
if(d < (thisRabbit.genes.vision / 2)+3){
thisRabbit.state = "foundFood";
this.acquiredFood = f;
found = true;
}
}
});
if(found){ return; }
thisRabbit.x += (noise(thisRabbit.t) * 4) - 2;
if(thisRabbit.x < 0){ thisRabbit.x = 0; }
if(thisRabbit.x > _world.width){ thisRabbit.x = _world.width; }
thisRabbit.y += (noise(thisRabbit.t + 5) * 4) - 2;
if(thisRabbit.y < 0){ thisRabbit.y = 0; }
if(thisRabbit.y > _world.height){ thisRabbit.y = _world.height; }
thisRabbit.t += 0.01;
}
SYNTAX ERRORS:
lines 23 / 24: assignment should be with a value (null or false)
this.genes = null;
this.acquiredFood = null;
lines 129 to 133: end you instructions with a ; instead of a ,
this.width = w;
this.height = h;
this.foodDensity = foodDensity;
this.food = [];
this.rabits = [];
line 156 to 160: there should be no space between rabbit and .t. Additionnally, because the coordinates are not directly linked to t, I would prefer to use random for starting position:
let x = this.width * random();
let y = this.height * random();
let _rabbit = new rabbit(x, y);
_rabbit.genes = genes;
_rabbit.t = t;

Canvas animation with JavaScript. Random coordinates and speed at every initiation

Edited : Thanks to all for valuable time and effort. Finally I made this )) JSfiddle
I was just playing with canvas and made this. Fiddle link here.
... some code here ...
var cords = [];
for(var i = 50; i <= width; i += 100) {
for(var j = 50; j <= height; j += 100) {
cords.push({ cor: i+','+j});
}
}
console.log(cords);
var offset = 15,
speed = 0.01,
angle = 0.01;
cords.forEach(function(e1) {
e1.base = parseInt(Math.random()*25);
e1.rgb = 'rgb('+parseInt(Math.random()*255)+','+parseInt(Math.random()*255)+','+parseInt(Math.random()*255)+')';
});
setInterval(function() {
cords.forEach(function(e1) {
e1.base = parseInt(Math.random()*25);
e1.rgb = 'rgb('+parseInt(Math.random()*255)+','+parseInt(Math.random()*255)+','+parseInt(Math.random()*255)+')';
});
},5000);
function render() {
ctx.clearRect(0,0,width,height);
cords.forEach(function(e1) {
//console.log(e1);
ctx.fillStyle = e1.rgb;
ctx.beginPath();
var r = e1.base + Math.abs(Math.sin(angle)) * offset;
var v = e1.cor.split(',');
ctx.arc(v[0],v[1],r,0,Math.PI * 2, false);
ctx.fill();
});
angle += speed;
requestAnimationFrame(render);
}
render();
Was wondering if -
Coordinates can be made random, now they are fixed as you can see. After 5000 mil, balls will show up in various random cords but even at their fullest they won't touch each other.
Every ball has same speed for changing size, I want that to be different too. Meaning, After 5000 mil, they show up with different animation speeds as well.
Also any suggestion on improving code and making it better/quicker/lighter is much appreciated. Thank you !
TL;DR - See it running here.
Making the coordinates random:
This requires you to add some random displacement to the x and y coordinates. So I added a random value to the coordinates. But then a displacement of less than 1 is not noticeable. So you'd need to magnify that random number by a multiplier. That's where the randomizationFactor comes in. I have set it to 100 since that is the value by which you shift the coordinates in each iteration. So that gives a truly random look to the animation.
Making Speed Random:
This one took me a while to figure out, but the ideal way is to push a value of speed into the array of coordinates. This let's you ensure that for the duration of animation, the speed will remain constant and that gives you a smoother feel. But again multiplying the radius r with a value between 0 and 1 reduces the speed significantly for some of the circles. So I have added a multiplier to 3 to compensate slightly for that.
Ideally I'd put a 2, as the average value of Math.random() is 0.5, so a multiplier of 2 would be adequate to compensate for that. But a little experimentation showed that the multiplier of 3 was much better. You can choose the value as per your preference.
Your logic of generating the coordinates changes as follows:
for(var i = 50; i <= width;i += 100) {
for(var j = 51; j <= height;j += 100) {
var x = i + (Math.random() - 0.5)*randomizationFactor;
var y = j + (Math.random() - 0.5)*randomizationFactor;
cords.push({ cor: x+','+y, speed: Math.random()});
}
}
Your logic of enlarging the circles changes as follows:
function render() {
ctx.clearRect(0,0,width,height);
cords.forEach(function(e1) {
//console.log(e1);
ctx.fillStyle = e1.rgb;
ctx.beginPath();
var r = e1.base + Math.abs(Math.sin(angle)) * offset * e1.speed * 3;
var v = e1.cor.split(',');
ctx.arc(v[0],v[1],r,0,Math.PI * 2, false);
ctx.fill();
});
angle += speed ;
requestAnimationFrame(render);
}
Suggestion: Update the coordinates with color
I'd probably also update the location of circles every 5 seconds along with the colors. It's pretty simple to do as well. Here I've just created a function resetCoordinates that runs every 5 seconds along with the setBaseRgb function.
var cords = [];
function resetCoordinates() {
cords = [];
for(var i = 50; i <= width;i += 100) {
for(var j = 51; j <= height;j += 100) {
var x = i + (Math.random() - 0.5)*randomizationFactor;
var y = j + (Math.random() - 0.5)*randomizationFactor;
cords.push({ cor: x+','+y, speed: Math.random()});
}
}
}
UPDATE I did some fixes in your code that can make your animation more dynamic. Totally rewritten sample.
(sorry for variable name changing, imo now better)
Built in Math.random not really random, and becomes obvious when you meet animations. Try to use this random-js lib.
var randEngine = Random.engines.mt19937().autoSeed();
var rand = function(from, to){
return Random.integer(from, to)(randEngine)
}
Internal base properties to each circle would be better(more dynamic).
var circles = [];
// better to save coords as object neither as string
for(var i = 50; i <= width; i += 100)
for(var j = 50; j <= height; j += 100)
circles.push({
coords: {x:i,y:j}
});
We can adjust animation with new bouncing property.
var offset = 15,
speed = 0.005,
angle = 0.01,
bouncing = 25;
This is how setBaseRgb function may look like
function setBaseRgb(el){
el.base = rand(-bouncing, bouncing);
el.speed = rand(5, 10) * speed;
el.angle = 0;
el.rgb = 'rgb('+rand(0, 255)+','+rand(0, 255)+','+rand(0, 255)+')';
}
All your animations had fixed setInterval timeout. Better with random timeout.
cords.forEach(function(el){
// random timeout for each circle
setInterval(setBaseRgb.bind(null,el), rand(3000, 5000));
})
You forgot to add your base to your circle position
function render() {
ctx.clearRect(0,0,width,height);
circles.forEach(function(el) {
ctx.fillStyle = el.rgb;
ctx.beginPath();
var r = bouncing + el.base + Math.abs(Math.sin(el.angle)) * offset;
var coords = el.coords;
ctx.arc(
coords.x + el.base,
coords.y + el.base,
r, 0, Math.PI * 2, false
);
ctx.fill();
el.angle += el.speed;
});
requestAnimationFrame(render);
}
render();
Effect 1 JSFiddle
Adding this
if(el.angle > 1)
el.angle=0;
Results bubling effect
Effect 2 JSFiddle
Playing with formulas results this
Effect 3 JSFiddle

Drawing animated lines

I'm trying to make the Logo Turtle in HTML 5 with javascript and canvas (I want to study simple algorithms with my students, and i want to make easy instructions).
I successfully made basic instructions, but the lines appear all at the same time, and i want to see them appear one after one.
Here is my code :
var dessin = document.getElementById("dessin")
var ctx = dessin.getContext("2d");
var angle = 0; // angle en degrés
// on donne les coordonnées de départ
var x = dessin.width / 2;
var y = dessin.height / 2;
function forward(distance) {
var iter = 1;
var Angle = angle % 360;
var theta = Angle / 180 * Math.PI;
var vitesse = 10;
var compteur = 1;
var timer = setInterval(function() {
ctx.beginPath();
ctx.moveTo(x, y);
x = Math.cos(theta) * distance / vitesse + x;
y = Math.sin(theta) * distance / vitesse + y;
ctx.lineTo(x, y);
ctx.stroke();
compteur++;
if (compteur > vitesse) {
clearInterval(timer);
}
}, 1000 / vitesse);
//setTimeout(clearInterval(timer),2000);
}
function turn_left(angle_rotation) {
angle = (angle - angle_rotation) % 360;
}
//Firing commands
turn_left(45);
forward(100);
turn_left(45);
forward(100);
<canvas id="dessin" width="400" height="400"></canvas>
I want to have two lines (a diagonal one, and a vertical one), but i have lot of it...
How can i do that ?
Thanks !
PS : I don't speak English very well, my apologies...
Your problem is your code is asynchrone. To achieve what you want to do, you need an animations manager.
Here, our animations manager is just two var : one is a boolean to know if we are moving, another is an array which accumulates queued animations:
var moveManager = [];
var isMoving = false;
I also make timer global because we have only one animation at a time :
var timer;
After you need to make the logic of your animations manager which is : If i'm not moving and I have a queued animation so play it :
function nextMove() {
if (!isMoving && moveManager.length) {
var move = moveManager.shift();
move();
}
}
And, last thing, each animation manages itself start, stop and call to next animation :
function turn_left(angle_rotation) {
moveManager.push(function() {
isMoving = true;
angle = (angle - angle_rotation) % 360 ;
isMoving = false;
nextMove();
});
}
With all this, you can continue your turtle.
Working jsFiddle => https://jsfiddle.net/y9efewqb/5/
PS : I use your code to explain how to make your turle but some part should be optimized (use requestAnimationFrame instead of using setInterval, make all this in classes to avoid global var, ...)

Water ripple - The image cannot load on first time

I follow the instructions to create water ripple effects, I replaced the background by my image. The effect run pretty good. But it have a problem. When load it first time, the browser cannot load my background (my image), you have to reload browser (F5) or enter again on address bar to see the effect. How to solve it?
My Code:
<!DOCTYPE html>
<html>
<head>
<title>Water Ripple HTML5</title>
<meta name="author" content="Brent Dingle">
<meta name="description" content="HTML5 canvas example of water ripple effect">
<style >
.waterCanvasStyle
{
border-width: 1px;
border-style: solid;
border-color:#a1a1d0;
border-radius: 8px;
box-shadow: #c6c6d0 4px 4px 10px;
}
</style>
</head>
<body>
<canvas id="waterCanvas0" width="400" height="400" >
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
var canvas = document.getElementById('waterCanvas0');
var ctx = canvas.getContext('2d');
var width = canvas.width;
var height = canvas.height;
var halfWidth = width >> 1;
var halfHeight = height >> 1;
var size = width * (height + 2) * 2; // space for 2 images (old and new), +2 to cover ripple radius <= 3
var delay = 30; // delay is desired FPS
var oldIdx = width;
var newIdx = width * (height + 3); // +2 from above size calc +1 more to get to 2nd image
var rippleRad = 3;
var rippleMap = [];
var lastMap = [];
var mapIdx;
// texture and ripple will hold the image data to be displayed
var ripple;
var texture;
// Any image can be used, but we will create a simple pattern instead
// So need some variables to create the background/underwater image
var stripeWidth = 25;
var step = stripeWidth * 2;
var count = height / stripeWidth;
canvas.width = width;
canvas.height = height;
var img = new Image();
img.src = "sea.jpg";
// Here is a neat trick so you don't have to type ctx.blah over and over again
with (ctx)
{
drawImage(img,0,0);
save();
restore();
}
// Initialize the texture and ripple image data
// Texture will never be changed
// Ripple is what will be altered and displayed --> see run() function
texture = ctx.getImageData(0, 0, width, height);
ripple = ctx.getImageData(0, 0, width, height);
// Initialize the maps
for (var i = 0; i < size; i++)
{
lastMap[i] = 0;
rippleMap[i] = 0;
}
// -------------------------------------------------------
// --------------------- Main Run Loop --------------
// -------------------------------------------------------
function run()
{
newframe();
ctx.putImageData(ripple, 0, 0);
}
// -------------------------------------------------------
// Drop something in the water at location: dx, dy
// -------------------------------------------------------
function dropAt(dx, dy)
{
// Make certain dx and dy are integers
// Shifting left 0 is slightly faster than parseInt and math.* (or used to be)
dx <<= 0;
dy <<= 0;
// Our ripple effect area is actually a square, not a circle
for (var j = dy - rippleRad; j < dy + rippleRad; j++)
{
for (var k = dx - rippleRad; k < dx + rippleRad; k++)
{
rippleMap[oldIdx + (j * width) + k] += 512;
}
}
}
// -------------------------------------------------------
// Create the next frame of the ripple effect
// -------------------------------------------------------
function newframe()
{
var i;
var a, b;
var data, oldData;
var curPixel, newPixel;
// Store indexes - old and new may be misleading/confusing
// - current and next is slightly more accurate
// - previous and current may also help in thinking
i = oldIdx;
oldIdx = newIdx;
newIdx = i;
// Initialize the looping values - each will be incremented
i = 0;
mapIdx = oldIdx;
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
// Use rippleMap to set data value, mapIdx = oldIdx
// Use averaged values of pixels: above, below, left and right of current
data = (
rippleMap[mapIdx - width] +
rippleMap[mapIdx + width] +
rippleMap[mapIdx - 1] +
rippleMap[mapIdx + 1]) >> 1; // right shift 1 is same as divide by 2
// Subtract 'previous' value (we are about to overwrite rippleMap[newIdx+i])
data -= rippleMap[newIdx + i];
// Reduce value more -- for damping
// data = data - (data / 32)
data -= data >> 5;
// Set new value
rippleMap[newIdx + i] = data;
// If data = 0 then water is flat/still,
// If data > 0 then water has a wave
data = 1024 - data;
oldData = lastMap[i];
lastMap[i] = data;
if (oldData != data) // if no change no need to alter image
{
// Recall using "<< 0" forces integer value
// Calculate pixel offsets
a = (((x - halfWidth) * data / 1024) << 0) + halfWidth;
b = (((y - halfHeight) * data / 1024) << 0) + halfHeight;
// Don't go outside the image (i.e. boundary check)
if (a >= width) a = width - 1;
if (a < 0) a = 0;
if (b >= height) b = height - 1;
if (b < 0) b = 0;
// Set indexes
newPixel = (a + (b * width)) * 4;
curPixel = i * 4;
// Apply values
ripple.data[curPixel] = texture.data[newPixel];
ripple.data[curPixel + 1] = texture.data[newPixel + 1];
ripple.data[curPixel + 2] = texture.data[newPixel + 2];
}
mapIdx++;
i++;
}
}
}
// -------------------------------------------------------
// Select random location to create drops
// So if user is doing nothing, water still
// gets ripples.
// -------------------------------------------------------
function randomDrop()
{
// Make it a little, irregular in timing
if ( Math.random() > 0.3 )
{
dropAt(Math.random() * width, Math.random() * height);
}
}
// -------------------------------------------------------
// Event handler for mouse motion
// -------------------------------------------------------
canvas.onmousemove = function(/* Event */ evt)
{
dropAt(evt.offsetX || evt.layerX, evt.offsetY || evt.layerY);
}
// -------------------------------------------------------
// Begin our infinite loop
// For user interaction and display updates
// -------------------------------------------------------
setInterval(run, delay);
// -------------------------------------------------------
// Create random ripples
// Note: this is NOT at same rate as display refresh
// -------------------------------------------------------
setInterval(randomDrop, 1250);
</script>
</body>
A common error when this type of issue occurs, is that you are trying to draw the image before it loads. Try utilizing the img.onload function to wait until the image has loaded to do all of the drawing and then to start running the main loop.

HTML5 Context/Canvas - when to call draw on the context

I'm writing a little object oriented style javasscript demo -- just to draw a bunch of balls moving around the screen. nothing fancy, no collision detection or anything at this point. Consider it safe to assume my Ball.js class is good.
My question amounts to this: Where should I call ball.draw(context) ? The only way to get balls drawn to the screen the way I set it up seems to be by placing the call in generateBalls(). But that means each ball is just drawn once.
So I'd really appreaciate it if someone could point out the error of my ways here. This isn't homework - just trying to get a better handle on javascript and canvas.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<script src="ball.js"></script>
<script src="utils.js"></script>
...
<canvas id="canvas" width="600" height="480"></canvas>
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded() {
canvasApp();
}
function canvasSupport() {
return true;
}
function canvasApp() {
if(!canvasSupport()) {
return;
}
}
console.log("app entered");
var numBalls = 45;
//var numBalls = demo.numberofballs.value;
var maxSize = 8;
var minSize = 5;
var maxSpeed = maxSize + 5;
var balls = new Array();
var tempBall;
var tempX;
var tempY;
var tempSpeed;
var tempAngle;
var tempRadius;
var tempRadians;
var tempXunits;
var tempYunits;
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
generateBalls();
setInterval(drawScreen, 33);
function generateBalls() {
console.log("Make some balls");
for(var index = 0; index < numBalls; index++) {
var tempRadius = Math.floor(Math.random()*maxSize)+minSize;
var ball = new Ball(tempRadius, "#000000");
ball.x = tempRadius * 2 + (Math.floor(Math.random()*canvas.width) - tempRadius * 2);
ball.y = tempRadius * 2 + (Math.floor(Math.random()*canvas.height) - tempRadius * 2);
ball.speed = maxSpeed - tempRadius;
ball.angle = Math.floor(Math.random()*360);
ball.dx = Math.cos(tempRadians) * tempSpeed;
ball.dy = Math.sin(tempRadians) * tempSpeed;
// here outputted balls but a stupid place to put it LOL
balls.push(ball);
}
}
function drawScreen() {
console.log("draw screen");
// loop through all balls and adjust their position
// a BallManager could do this more cleanly
for(var index = 0; index < balls.length; index++) {
context.fillStyle="#EE00EE";
context.fillRect(0,0,canvas.width, canvas.height);
// Box
context.strokeStyle = "#ff0043";
context.strokeRect(1,1,canvas.width-2, canvas.height-2);
// place balls
context.fillStyle = "#ff8783";
console.log("ball mover loop in drawscreen");
// no var ball now
ball = balls[index];
ball.x += ball.dx;
ball.y += ball.dy;
ball.draw(context);
//checkBoundaries(balls[index]);
if(ball.x > canvas.width || ball.x < 0) {
ball.angle = 180 - ball.angle;
updateBall(ball);
} else if(ball.y > canvas.height || ball.y < 0) {
ball.angle = 360 - ball.angle;
updateBall(ball);
//ball.draw(context);
}
}
}
//function checkBoundaries(ball) {
//console.log("Check Bounds: " + " " + "ball.x: " + ball.x + " " + //"ball.y: " + ball.y);
//}
function updateBall(ball) {
ball.radians = ball.angle * Math.PI / 180;
ball.dx = Math.cos(ball.radians) * ball.speed;
ball.dy = Math.sin(ball.radians) * ball.speed;
//ball.draw(context);
}
</script>
</body>
</html>
Thank you for your advice,
Marc
Your example contains more than one error, please check your modified code. It works, but you must extend and correct it.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<script type="text/javascript">
// next lines is a Ball() implementation code
Ball = function(radius,color) {
this.radius=radius;
this.color=color;
};
Ball.prototype.x=0;
Ball.prototype.y=0;
Ball.prototype.speed=0;
Ball.prototype.angle=0;
Ball.prototype.dx=0;
Ball.prototype.dy=0;
Ball.prototype.radius=10;
Ball.prototype.color="#000";
Ball.prototype.draw=function() {
context.beginPath();
context.arc(this.x,this.y,this.radius,0,Math.PI*2,true);
context.lineWidth = 5;
context.strokeStyle = this.color; // line color
context.stroke();
context.closePath();
};
window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded() {
canvasApp();
//console.log("app entered");
window.canvas = document.getElementById("canvas");
window.context = canvas.getContext("2d");
generateBalls();
// if you want to use setInterval() instead replace next line
setTimeout(drawScreen, 33);
}
function canvasSupport() {
return true;
}
function canvasApp() {
if(!canvasSupport()) {
return;
}
}
var numBalls = 45;
//var numBalls = demo.numberofballs.value;
var maxSize = 8;
var minSize = 5;
var maxSpeed = maxSize + 5;
var balls = new Array();
var tempBall;
var tempX;
var tempY;
var tempSpeed;
var tempAngle;
var tempRadius;
var tempRadians;
var tempXunits;
var tempYunits;
function generateBalls() {
//console.log("Make some balls");
for(var index = 0; index < numBalls; index++) {
var tempRadius = Math.floor(Math.random()*maxSize)+minSize;
var tempRadians = Math.random()*Math.PI;
var tempSpeed = 10;
var ball = new Ball(tempRadius, "#000000");
ball.x = tempRadius * 2 + (Math.floor(Math.random()*canvas.width) - tempRadius * 2);
ball.y = tempRadius * 2 + (Math.floor(Math.random()*canvas.height) - tempRadius * 2);
ball.speed = maxSpeed - tempRadius;
ball.angle = Math.floor(Math.random()*360);
ball.dx = Math.cos(tempRadians) * tempSpeed;
ball.dy = Math.sin(tempRadians) * tempSpeed;
// here outputted balls but a stupid place to put it LOL
balls.push(ball);
}
}
function drawScreen() {
console.log("draw screen");
context.fillStyle="#EE00EE";
context.fillRect(0,0,canvas.width, canvas.height);
// Box
context.strokeStyle = "#ff0043";
context.strokeRect(1,1,canvas.width-2, canvas.height-2);
// loop through all balls and adjust their position
// a BallManager could do this more cleanly
for(var index = 0; index < balls.length; index++) {
// place balls
context.fillStyle = "#008700";
//console.log("ball mover loop in drawscreen");
// no var ball now
ball = balls[index];
ball.x += ball.dx;
ball.y += ball.dy;
ball.draw(context);
//checkBoundaries(balls[index]);
if(ball.x > canvas.width || ball.x < 0) {
ball.angle = 180 - ball.angle;
updateBall(ball);
} else if(ball.y > canvas.height || ball.y < 0) {
ball.angle = 360 - ball.angle;
updateBall(ball);
//ball.draw(context);
}
}
// if you want to use setInterval() instead remove next line
setTimeout(drawScreen, 33);
}
//function checkBoundaries(ball) {
//console.log("Check Bounds: " + " " + "ball.x: " + ball.x + " " + //"ball.y: " + ball.y);
//}
function updateBall(ball) {
ball.radians = ball.angle * Math.PI / 180;
ball.dx = Math.cos(ball.radians) * ball.speed;
ball.dy = Math.sin(ball.radians) * ball.speed;
//ball.draw(context);
}
</script>
</head>
<body>
<canvas id="canvas" width="600" height="480" style="background:red;"></canvas>
</body>
</html>
http://jsfiddle.net/QVgZx/2/
Just necroing the thread to add a bit of an update for anyone new to canvas who arrives here:
Stan recommended creating a function to draw each of the balls at one spot in code; this is good for a number of reasons, it makes the animation smoother, and the code far easier to debug/maintain.
I would disagree with Stan, however, when it comes to using setInterval to trigger this... although, that might very well have been common practice when Stan wrote it.
Now, however, we use requestAnimationFrame.
This will sync your painting with the rest of the browser.
This allows your code to run far faster since every time the browser draws something, it has to go through the entire page again to determine where everything will go, just in case something moved.
Using setInterval leaves no guarantee as to when it will fire, and certainly doesn't time it to coincide with the browser's screen refresh.
This means it is drawing a little, reconfiguring the page, drawing a little more, reconfiguring again, drawing some more... and again and again. This is very bad, and very slow.
Using requestAnimationFrame, however, allows the browser to call your function whenever it is about to redraw the screen anyway... which means a single redraw, and a single refresh.
Far faster, far cleaner.
It works slightly differently, but is actually very simple.
requestAnimationFrame(redraw);
This registers your function 'redraw' with requestAnimationFrame so that the next time the browser wants to redraw the window, your function will be called.
The one gotcha with this is that it will only call your function ONCE... in that way, it is like a setTimeout rather than a setInterval.
This way, you don't need to pass around the timer variable to stop the animation; it will stop because it stopped being called.
However, to ensure that your animation continues to run, just put that very same call at the bottom of your redraw function:
function redraw()
{
var blah, blahblah;
...
requestAnimationFrame(redraw);
}
You can also set this conditionally, to run your animation until it is finished, then stop:
function redraw()
{
...
if (!finished) requestAnimationFrame(redraw);
}
Mozilla reference HERE, Paul Irish HERE, and Chris Coyer HERE.

Categories