I have a small program that I am supposed to write that makes a bouncy ball in a canvas. I can get a wireframe of a ball bouncing, but can't seem to get the setTimeout to fire at all. I have read, read and read about the function, but can't figure this out (new).
<!DOCTYPE HTML>
<html>
<head>
<title>basic Canvas</title>
<style>
#canvas1{
border:1px solid #9C9898;
}
body{
margin:0px;
padding:0px;
}
</style>
<script>
function drawMe(){
//Set x,y,radius
var x = 60;
var y = 60;
var radius = 70;
drawLoop(x,y,radius);
}
function drawLoop(x,y,radius){
var canvas2=document.getElementById("canvas1");
var ctx=canvas2.getContext("2d");
for(i=1;i<100;i++){
if(y + radius >= canvas2.height){
d = 1;
}
if(y - radius <= 0){
d = 0;
}
if (d==0){
x = x + 10;
y = y + 10;
}
else if (d==1){
x = x + 10;
y = y - 10;
}
draw(x,y,radius);
window.setTimeout(function() {draw(x,y,radius)},3000);
}
}
function draw(x,y,radius){
var canvas2=document.getElementById("canvas1");
var ctx=canvas2.getContext("2d");
ctx.beginPath();
ctx.arc(x,y,radius,0,2*Math.PI,false);
var gradient = ctx.createRadialGradient(x, y, 1, x, y, radius);
gradient.addColorStop(0,"blue");
gradient.addColorStop(1,"white");
ctx.fillStyle=gradient;
ctx.lineWidth=1;
ctx.strokeStyle="blue";
ctx.fill();
ctx.stroke();
}
</script>
</head>
<body onload="drawMe()">
<canvas id="canvas1" width=1000" height="400">
</canvas>
</body>
</html>
A little function called 'drawMe()' which sets x, y, and radius, then calls a little drawing loop that fires 100 times that draws the bouncy ball ('drawLoop'). at the bottom of the function drawLoop, I call draw, which actually drawls the circles. From what I've read, the line 'setTimeout(function(){draw(x,y,radius)};,3000); should call the draw function every three seconds. But it doesn't. What the heck am I doing wrong?
setTimeouts are counted from the time they are created. The loop runs almost instantly and creates the setTimeouts at almost the same time. They are then all ran 3 seconds later.
One way to get around this is in the solution below. This does not increment the loop until the current timeout has been completed.
http://jsfiddle.net/x8PWg/14/
This is only one of the many potential solutions to this.
Related
I am trying to assign the global variables mouseX, mouseY to an event listener inside a function. When I try to use the variables inside another function, they come back undefined.
I know it's probably a problem with the scope of the variables being assigned to, but have not been able to figure it out, and any simple test code I write doesn't reproduce the problem.
What is causing this?
javascript:
var mouseX, mouseY;
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var bars = [];
for(var i = 0; i < 30; i++)
bars[i] = new Bar(ctx, 100+i*10, canvas.height, 5, 150);
for(var i = 0; i < bars.length; i++){
bars[i].checkMouse();
bars[i].display();
}
function Bar(ct, x, y, w, h){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.y -= this.h;
this.display = function() {
ct.rect(this.x, this.y, this.w, this.h);
ct.fill();
}
this.checkMouse = function() {
//if(mouseX >= this.x && mouseX <= this.x + this.w){
//this.y = mouseY; this.h = height-mouseY;
console.log(mouseX); //////mouseX is undefined?
//}
}
}
function getMouse(event) {
mouseX = event.offsetX;
mouseY = event.offsetY;
var coords = "mouseX: " + mouseX + ", mouseY: " + mouseY;
document.getElementById('display').innerHTML = coords;
}
HTML:
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
canvas{
background-color: #e6e6e6;
position:relative;
}
</style>
</head>
<body>
<div id="display"> </div>
<canvas onmousemove="getMouse(event)" id="myCanvas" width="500" height="200">
<script src="myscript.js"></script>
</body>
</html>
as h2oooooo pointed out:
This loop was running just once:
for(var i = 0; i < bars.length; i++){
bars[i].checkMouse();
bars[i].display();
}
When it is supposed to behave like a main loop. Copying it into the getMouse() function fixed it.
In this case it's not a problem with the scopeing, but a problem with the order of execution of your code fragments.
A script file gets executed, when loaded completely. So all your definitions, the Creation of your objects and invokes the method which doesn't work in your example.
The function, in which mouseX and mouseY get initialized, gets executed in the mouseover Event of the canvas and though gets executed later.
So all in all: When your method is fired, the variables are uninitialized, because they get initialized in a function, which is executed later.
The best idea would be, to couple the execution of the method which is problematic to the execution of the getMouse function.
Please note that relying on such a global state, especially when creating such race conditions, is a very bad style, because it leads to issues like that and gets nearly unmaintainable when your project grows.
I'm trying to build a basic renderer that can update the HTML canvas in real time or on a frame by frame basis.
For example, as soon as a pixel is drawn, it'll either update right away to the screen or wait to be collected for a frame, which is then updated (like 25 frames per second)
At the moment this doesn't happen in any way with my code, and I am not sure how to get it to work.
Right now, the canvas only draws to the screen once all operations are complete.
Canvas.js:
$(document).ready(function(){
main();
});
function main(){
var c = document.getElementById('canvas');
var ctx = c.getContext("2d");
for(var x=0; x<1000000; x++){
drawPixel(ctx, "200", "200", "200", "255", randomNumber(0, 1000), randomNumber(0, 600));
}
}
function drawPixel(context, red, green, blue, alpha, x, y){
context.fillStyle = "rgba(" + red + ", " + green + ", " + blue + ", " + (alpha/255) + ")";
context.fillRect(x, y, 1, 1);
}
function randomNumber(min, max){
return Math.floor(Math.random()*(max-min+1)+min);
}
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Canvas Test</title>
<link rel="stylesheet" type="text/css" href="styles.css" media="screen"></style>
<meta name="viewport" content="initial-scale=1">
</head>
<body>
<div class="container">
<canvas height="600" width="1000" id="canvas">
</canvas>
</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
<script type="text/javascript" src="canvas.js"></script>
</body>
</html>
I need to know how to make the screen update when I want, instead of it just updating all at once after my code has finished.
EDIT:
Basically, what I want is all the dots to appear gradually so the user can see the dots accumulating. Is there a way I can collect the dots every 25th of a second, render that as a frame and repeat?
You just need to break the "synchronousness" of your for loop as the browser is completing all drawPixel calls in the same repaint.
Instead, use requestAnimationFrame to call a function once at the browser's natural repaint interval (this, instead of setTimeout or similar). something similar to:
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
var counter = 0;
function run() {
drawPixel(0, 0, 0, 255, randomNumber(0, canvas.width), randomNumber(0, canvas.height));
counter++;
// If our counter is less than 10000, run again at the next repaint
if (counter < 10000) {
window.requestAnimationFrame(run);
}
}
function drawPixel(red, green, blue, alpha, x, y){
context.fillStyle = "rgba(" + red + ", " + green + ", " + blue + ", " + (alpha/255) + ")";
context.fillRect(x, y, 1, 1);
}
function randomNumber(min, max){
return Math.floor(Math.random()*(max-min+1)+min);
}
run();
<canvas height="100" width="100" id="canvas"></canvas>
The requestAnimationFrame API can be used to allow the browser to decide when it's time to redraw. The problem you have to solve is that your JavaScript likely takes much longer than the 16 milliseconds or so between frames.
There are a variety of ways to handle this, but most of them are going to involve executing the draw commands in batches. Here is one possible solution:
// These variables would need to be outside the main function scope
var c = document.getElementById('canvas');
var ctx = c.getContext("2d");
var batchSize = 1000;
var currentItem = 0;
var lastItem = 1000000;
// Execute a batch of draw commands
function main() {
// Queue the next frame
window.requestAnimatoinFrame(main);
// Draw the pixels
for(var batchEnd = currentItem + batchSize; currentItem < batchEnd; currentItem++) {
drawPixel(ctx, ...);
}
}
// Queue the first frame
window.requestAnimationFrame(main);
Try an interval timer with a limited number updates in each iteration, as in:
$(document).ready(function(){
main();
});
function main(){
var c = document.getElementById('canvas');
var ctx = c.getContext("2d");
var pixelsPerTick = 1000;
// one thousand iterations each tick
window.setInterval(function(){
for(var x=0; x<pixelsPerTick; x++){
drawPixel(ctx, "200", "200", "200", "255", randomNumber(0, 1000), randomNumber(0, 600));
}
}, 1000 / 25); // 25 times per second
}
function drawPixel(context, red, green, blue, alpha, x, y){
context.fillStyle = "rgba(" + red + ", " + green + ", " + blue + ", " + (alpha/255) + ")";
context.fillRect(x, y, 1, 1);
}
function randomNumber(min, max){
return Math.floor(Math.random()*(max-min+1)+min);
}
I want to create a game where I need a animation: First should drawed a rectangle after 5 seconds, the second rect after 5 seconds, the third after 5 too, the fourth after 5 too, the 6-10 rects after 4s, the 10-15 rects after 3s, the 15-20 rects after 2s and the 20-25 rects after 1 second. The rectangles came from above and should run with a speed called recty to the bottom. Maybe will this help:jsfiddle.
var x = canvasWidth / 100;
var y = canvasHeight / 100;
b = 5000;
function init() {
recty = canvasHeight / 100 * 20;
rectx = (Math.random() *(x * 50)) + (x / 5);
rectb = (Math.random() * (x * 40)) + x * 20;
return setInterval(main_loop, 10);
}
function draw() {
rectheight = canvasHeight / 100 * 10;
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
// draw triangles
ctx.beginPath();
ctx.moveTo(x * 90, y * 50);
ctx.lineTo(x * 99, y * 60);
ctx.lineTo(x * 99, y * 40);
ctx.closePath();
ctx.stroke();
}
function drawrect() {
// draw rect
ctx.beginPath();
fillStyle = "#000000";
ctx.rect(rectx, recty, rectb, rectheight);
ctx.fill();
}
function update() {
recty += 1;
if (recty > canvasHeight) {
recty = -rectheight;
rectx = (Math.random() *(x * 50)) + (x / 5);
rectb = (Math.random() *(x * 50)) + (x / 5);
b -=1000;
}
if (recty > canvasHeight) {
recty -= 1;
}
}
function main_loop() {
draw();
update();
collisiondetection();
drawrect();
}
init();
setInterval ( drawrect, b );
Modern browsers have a built-in timer: requestAnimationFrame.
A requestAnimationFrame loop will fire about every 16ms and will be given a very precise currentTime argument. You start the timing loop with: requestAnimationFrame(Timer);. The loop will execute only once for each requestAnimationFrame you issue, so you put a requestAnimationFrame inside the loop itself to keep it running.
Here's an example timing loop that calculates the elapsed time since the timing loop started:
// variable used to calculate elapsed time
var lastTime;
// start the first timing loop
requestAnimationFrame(Timer);
function Timer(time){
// request another timing loop
// Note: requestAnimationFrame fires only once,
// so you must request another loop inside
// each current loop
requestAnimationFrame(Timer);
// if this is the very first loop, initialize `lastTime`
if(!lastTime){lastTime=time;}
// calculate elapsed time since the last loop
var elapsedTime=time-lastTime;
}
To make your rectangles "time aware" you can create a javascript object for each rectangle that defines all that's need to draw that rectangle at the desired timing interval. Then use this javascript object to draw the rectangle at the desired position after the desired time interval.
Example of rectangle object properties
position of the rect: x,y
the time interval to wait before next updating the rect's position: interval
the distance to move the rect during an update: moveByX, moveByY
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var rects=[]
rects.push({x:10,y:10,moveByX:5,interval:500,nextMoveTime:0});
rects.push({x:10,y:50,moveByX:5,interval:1000,nextMoveTime:0});
rects.push({x:10,y:110,moveByX:5,interval:2000,nextMoveTime:0});
var isFirstLoop=true;
// start the timing loop
requestAnimationFrame(Timer);
function Timer(currentTime){
// request another timing loop
// Note: requestAnimationFrame fires only once,
// so you must request another loop inside
// each current loop
requestAnimationFrame(Timer);
if(isFirstLoop){
isFirstLoop=false;
for(var i=0;i<rects.length;i++){
rects[i].nextMoveTime=time+rects[i].interval;
}
}
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<rects.length;i++){
drawRect(rects[i],currentTime);
}
}
function drawRect(r,time){
if(time>r.nextMoveTime){
r.x+=r.moveByX;
r.nextMoveTime=parseInt(time+r.interval);
}
ctx.strokeRect(r.x,r.y,110,15);
ctx.fillText('I move every '+r.interval+'ms',r.x+5,r.y+10);
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>
Have you though about css animation?
It can come really handy, and has a better performance than javascript.
You can use transitions on position, and even delay if you don't care about IE9 and before. If you do, you should initiate the animations with javascript by adding a class to each boxes, and that would make it cross-browser.
Bassic css mockup for this would look something like this:
.box{
width:90px;height:90px;background:red;position:absolute;
-webkit-animation: mymove 5s; /* Chrome, Safari, Opera */
animation: mymove 5s;
animation-delay: 2s;
-webkit-animation-delay: 2s; /* Chrome, Safari, Opera */
}
And then you can add rules for specific boxes, and overrule the basic box style.
.box-1{animation-delay:10s; -webkit-animation-delay: 2s;}
See jsfiddle: http://jsfiddle.net/eyqtg9wp/
Hello I'm trying to create a rectangle that grows from the side of the canvas until it fills the whole canvas, once it has done that shrink back to is original state, the approach I'm taking is using requestAnimationFrame /cancelAnimationFrame for some reason I'm not sure cancelAnimationFrame does not seem to work my code is the following one :
<script>
function grRectangle(){
var canvas = document.getElementById("paper");
var context= canvas.getContext("2d");
//var forpi = Math.PI * 2;
//context.fillStyle = "black";
context.fillRect(0,0,canvas.width,canvas.height);
var posX = 200;
var posY = 100;
var color = 0;
function draw(){
context.fillStyle = 'hsl('+ color++ + ',100%,50%)';
context.beginPath();
context.rect(0,0,posX,posY);
context.fill();
posX = posX + 0.9;
posY = posY + 0.9;
if(posX < canvas.width ){
requestAnimationFrame(draw);
} if (posX >= canvas.width){
posX = posX - 0.9;
posY = posY - 0.9;
cancelAnimationFrame(draw);
}
}
draw();
};
</script>
<body onload= "grRectangle();" >
<h1>Growing Rectangle</h1>
<canvas id = "paper" width="800" height="600">
</canvas>
Any help is kindly appreciatted
It seems to me like the code you wrote doesn't actually need a cancelAnimationFrame. I am not sure what you think it does exactly, but it seems like you misunderstood it.
The cancelAnimationFrame method is used to prevent a previous call to requestAnimationFrame from getting executed, as long as this didn't happen yet. There are really few situations where you need this.
In your case I would put that growth-per-frame constant of 0.9 into a variable. When the rectangle size reaches the upper bound, just change it to -0.9 and it will get smaller again. When it reaches the lower bound, change it again to 0.9 and it will grow again.
You will, however, not see that shrinking, because you aren't erasing your canvas. Every frame is drawn on top of the previous one. You will have to erase your canvas at the beginning of your drawing loop. To do that, move the code which fills the canvas with a black rectangle into the drawing loop (remember to set the fill-style to black).
I tried both of these in canvas and nothing showed, also I doubt it is even efficient :/. I am trying to make rain that comes down the screen.. Wondering what is the most efficient way of doing this. I am a beginner at animation and would really appreciate help.
I suspect that creating a rain object would be best, each with the quality of coming down the screen then coming to the top and then an array with them...maybe with random x values withing the canvas width and y values of 0 but I don't know how to implement that. Please help!
xofRain = 20;
startY = 0;
ctx.beginPath();
ctx.moveTo(xofRain, startY);
ctx.lineTo(xofRain, startY + 20);
ctx.closePath();
ctx.fillStyle = "black";
ctx.fill();
function rain(xofRain){
startY = canvas.height();
ctx.moveTo(xofRain, startY);
ctx.beginPath();
ctx.lineTo(xofRain, startY + 3);
ctx.closePath();
ctx.fillStyle = "blue";
ctx.fill();
}
Here comes your answer, this snow rain is created using pure HTML5 Canvas, the technique used to achieve this animation is called "Double Buffer Animation". First it is good to know what is Double Buffer animation technique.
Double Buffer Technique: This is an advanced technique to make animation clear and with less flickers in it. In this technique 2 Canvas is used, one is displayed on webpage to show the result and second one is used to create animation screens in backed process.
How this will help full, suppose we have to create a animation with very high number of move, as in our Snow Fall example, there are number of Flakes are moving with there own speed, so keep them moving, we have to change position of each flake and update it on the canvas, this is quite heavy process to deal with.
So Now instead of updating each Flake directly on our page canvas, we will create a buffer Canvas, where all these changes take place and we just capture a Picture from Buffer canvas after 30ms and display it on our real canvas.
This way our animation will be clear and without flickers. So here is a live example of it.
http://aspspider.info/erishaan8/html5rain/
Here is the code of it:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>HTML5 Rain</title>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<style>
article, aside, figure, footer, header, hgroup,
menu, nav, section { display: block; }
</style>
<script type="text/javascript">
var canvas = null;
var context = null;
var bufferCanvas = null;
var bufferCanvasCtx = null;
var flakeArray = [];
var flakeTimer = null;
var maxFlakes = 200; // Here you may set max flackes to be created
function init() {
//Canvas on Page
canvas = document.getElementById('canvasRain');
context = canvas.getContext("2d");
//Buffer Canvas
bufferCanvas = document.createElement("canvas");
bufferCanvasCtx = bufferCanvas.getContext("2d");
bufferCanvasCtx.canvas.width = context.canvas.width;
bufferCanvasCtx.canvas.height = context.canvas.height;
flakeTimer = setInterval(addFlake, 200);
Draw();
setInterval(animate, 30);
}
function animate() {
Update();
Draw();
}
function addFlake() {
flakeArray[flakeArray.length] = new Flake();
if (flakeArray.length == maxFlakes)
clearInterval(flakeTimer);
}
function blank() {
bufferCanvasCtx.fillStyle = "rgba(0,0,0,0.8)";
bufferCanvasCtx.fillRect(0, 0, bufferCanvasCtx.canvas.width, bufferCanvasCtx.canvas.height);
}
function Update() {
for (var i = 0; i < flakeArray.length; i++) {
if (flakeArray[i].y < context.canvas.height) {
flakeArray[i].y += flakeArray[i].speed;
if (flakeArray[i].y > context.canvas.height)
flakeArray[i].y = -5;
flakeArray[i].x += flakeArray[i].drift;
if (flakeArray[i].x > context.canvas.width)
flakeArray[i].x = 0;
}
}
}
function Flake() {
this.x = Math.round(Math.random() * context.canvas.width);
this.y = -10;
this.drift = Math.random();
this.speed = Math.round(Math.random() * 5) + 1;
this.width = (Math.random() * 3) + 2;
this.height = this.width;
}
function Draw() {
context.save();
blank();
for (var i = 0; i < flakeArray.length; i++) {
bufferCanvasCtx.fillStyle = "white";
bufferCanvasCtx.fillRect(flakeArray[i].x, flakeArray[i].y, flakeArray[i].width, flakeArray[i].height);
}
context.drawImage(bufferCanvas, 0, 0, bufferCanvas.width, bufferCanvas.height);
context.restore();
}
</script>
</head>
<body onload="init()">
<canvas id="canvasRain" width="800px" height="800px">Canvas Not Supported</canvas>
</body>
</html>
Also if you find this help full, accept as Answer and make it up. o_O
Cheers!!!
I'm not sure what "most efficient" is. If it was me I'd do it in WebGL but whether or not that's efficient is not clear to me.
In either case I'd try to use a stateless formula. Creating and updating state for every raindrop is arguably slow.
const ctx = document.querySelector("canvas").getContext("2d");
const numRain = 200;
function render(time) {
time *= 0.001; // convert to seconds
resizeCanvasToDisplaySize(ctx.canvas);
const width = ctx.canvas.width;
const height = ctx.canvas.height;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, width, height);
resetPseudoRandom();
const speed = time * 500;
ctx.fillStyle = "#68F";
for (let i = 0; i < numRain; ++i) {
const x = pseudoRandomInt(width);
const y = (pseudoRandomInt(height) + speed) % height;
ctx.fillRect(x, y, 3, 8);
}
requestAnimationFrame(render);
}
requestAnimationFrame(render);
let randomSeed_ = 0;
const RANDOM_RANGE_ = Math.pow(2, 32);
function pseudoRandom() {
return (randomSeed_ =
(134775813 * randomSeed_ + 1) %
RANDOM_RANGE_) / RANDOM_RANGE_;
};
function resetPseudoRandom() {
randomSeed_ = 0;
};
function pseudoRandomInt(n) {
return pseudoRandom() * n | 0;
}
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
}
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas></canvas>
Note that I could have used ctx.moveTo(x, y); ctx.lineTo(x, y + 8); for each line and then at the end of the loop called ctx.stroke(). I didn't do that because I'm assuming it would be less efficient than using ctx.fillRect. In order for the canvas to draw lines it actually has to allocate a dynamic path (you call ctx.beginPath). It then has to record all the lines you add. Then it has to expand those lines into vertices of various kinds to rasterize the lines. You can basically see the various algorithms it uses here. Conversely none of that has to happen with ctx.fillRect. No allocations have to happen (not saying they don't happen, just saying they don't have to). The canvas can just use a single pre-allocated quad and draw it on the GPU by passing the correct matrix to draw whatever rectangle you ask of it. Of course they're might be more overhead calling ctx.fillRect 200 times rather than ctx.moveTo, ctx.lineTo 200s + ctx.stroke once but really that's up to the browser.
The rain above may or may not be a good enough rain effect. That wasn't my point in posting really. The point is efficiency. Pretty much all games that have some kind of rain effect do some kind of stateless formula for their rain. A different formula would generate different or less repetitive rain. The point is it being stateless.