setTimeout() and canvas not working as intended - javascript

Here's my code. I created a VIBGOYR pattern on the canvas and want to change the radial gradient of the system after a time interval so that it looks animated.
function activate(index){
canvas =document.getElementById("exp");
context = canvas.getContext('2d');
//Start.
draw(index);
//Functions.
function draw(index){
drawGradiantSq(index);
var t=setTimeout(doTimer,1000);
}
function doTimer(){
draw(index+10);
}
function drawGradiantSq(fi){
console.log(fi);
var g1 = context.createRadialGradient(0,300,0,500,300,fi);
colors = ["violet","indigo","blue","green","orange","yellow","red"];
var x= 1/12;
for (var i=0;i<colors.length;i++){
g1.addColorStop(i*x,colors[i]);
}
context.fillStyle = g1;
context.fillRect(0,0,500,600);
var g2 = context.createRadialGradient(1000,300,0,500,300,fi);
for (var i=0;i<colors.length;i++){
g2.addColorStop(i*x,colors[i]);
}
context.fillStyle = g2;
context.fillRect(500,0,1000,600);
}
}
But the output i'm getting only changes the gradient only once although the timer seems to be working fine.
The HTML file:
<body>
<canvas id="exp" width="1000px" height="600px"></canvas>
<button onclick="activate(index+=10);">Paint</button>
</body>
The variable index is a global variable set to 0.

Try using setInterval instead of setTimeout.
setTimeout is only triggered once:
'setInterval' vs 'setTimeout'

Related

Javascript function from a JS file doesn't get called from the canvas

I'm trying to learn about JavaScript and the html5 canvas, however, it's proving a little confusing and I don't understand why it doesn't seem to work...
I am working on creating a simple map that has some of the capabilities of google maps(drag and drop/zoom in/out/etc). In order to do this, I chose html5 canvas and easeljs for the drag and drop functions.
I have a javascript file (path.js) which contains 2 functions:
pathConstructor() - example function from the easeljs tutorial
drawMap() - copy of the first function slightly modified (and probably wrong right now)
Everything worked fine when I called pathConstructor() from the canvas, however, after I replaced it with drawMap(), everything stopped working. It won't even work if I replace drawMap() with pathContrcutor() right now.
I put some alerts before and after calling the function from the canvas and inside pathConstructor(). The before alert goes off but the others don't so for some reason the function never gets called...
If I use the pathConstructor code as inline code in the canvas then it works just fine, however, I would like to avoid that since I believe it's bad programming. I want it to be neat and each script to have its own file.
Anyone know why this is happening?
HTML
<!Doctype html>
<html>
<head>
<script src="http://code.createjs.com/easeljs-0.7.0.min.js"></script>
<script src="path.js" type="text/javascript"></script>
</head>
<body>
<canvas id="canvas" width="1300px" height="800px"style="border:1px dotted black;">
<script>pathConstructor();</script>
</canvas>
</body>
</html>
Javascript
var stage;
function pathConstructor() {
alert('inside pathConstructor');
stage = new createjs.Stage('canvas');
// this lets our drag continue to track the mouse even when it leaves the canvas:
// play with commenting this out to see the difference.
stage.mouseMoveOutside = true;
var circle = new createjs.Shape();
circle.graphics.beginFill("red").drawCircle(0, 0, 50);
var label = new createjs.Text("drag me", "bold 14px Arial", "#FFFFFF");
label.textAlign = "center";
label.y = -7;
var dragger = new createjs.Container();
dragger.x = dragger.y = 100;
dragger.addChild(circle, label);
stage.addChild(dragger);
dragger.on("pressmove", function(evt) {
// currentTarget will be the container that the event listener was added to:
evt.currentTarget.x = evt.stageX;
evt.currentTarget.y = evt.stageY;
// make sure to redraw the stage to show the change:
stage.update();
});
stage.update();
}
function drawMap() {
stage = new createjs.Stage('canvas');
var bitMap = new createjs.Bitmap('middle-earth-map.jpg');
stage.mouseMoveOutside = true;
var dragger = new createjs.Container();
dragger.x = dragger.y = 0;
dragger.addChild(bitMap);
stage.addChild(dragger);
dragger.on('pressmove', function(evt2)) {
evt2.currentTarget.x = evt2.stageX;
evt2.currentTarget.y = evt2.stageY;
stage.update();
});
stage.update();
}
For me it's working fine, you just have to remove that extra ")" in dragger.on('pressmove', function(evt2)) {;
dragger.on('pressmove', function(evt2)) {
evt2.currentTarget.x = evt2.stageX;
evt2.currentTarget.y = evt2.stageY;
stage.update();
});

html canvas - issue with moving image

I am learning html canvas and below is my html code.
<!DOCTYPE html>
<html>
<head>
<script language="javascript">
function moveImage(x) {
var context = document.getElementById('myCanvas').getContext("2d");
var img = new Image();
img.onload = function () {
context.drawImage(img, x, 259);
}
img.src = "flower.jpg";
}
function startDrawing() {
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(50, 300);
context.lineTo(950, 300);
context.stroke();
var x=50;
setInterval(function() {
x = x+20;
moveImage(x);
}, 1000);
}
</script>
</head>
<body onload="startDrawing()">
<canvas id="myCanvas" width="1000" height="1000">
</body>
</html>
Please find the below output from this code:
How can I remove the traces of 'older frames' (of the flower), as you could see lots of flowers while it is moving from left to right in the screen shot ? Please help the code changes required.
Thanks.
The problem is you aren't clearing the canvas before drawing on it again. You can clear it using clearRect.
setInterval(function() {
// Clear the canvas
context.clearRect(0, 0, canvas.width, canvas.height);
x += 20;
moveImage(x);
}, 1000);
Keep in mind that this will clear the entire canvas. If you're rendering anything else, you'll want to redraw it after the clear.
You need to re-render the frame. Here's some code from an incomplete game I wrote:
var main = function () {
var now = Date.now(),
delta = now - then;
update(delta / 1000);
render();
then = now;
// Request to do this again ASAP
requestAnimationFrame(main);
};
GitHub repo code
My render function contains the context.drawImg() work (getting properly re-rendered), and that's similar to your moveImage function.
Edit: A little explanation. The image traces are previous renderings of the your image at each updated position. Without the frame itself being reset, each move of the image is preserved on the screen, giving the appearance of a trail of images.

Using the Canvas for web animation results in nothing happening?

I'm learning javascript, so a coworker is having me do basic animation in the browser. I just finished my code, using the canvas element to display images to the screen, but nothing happens when I run the code. My debug statements don't get printed to the console, nor do any errors. Can anyone see a problem with my code?
<html>
<body>
<canvas id="console" width="600" height="600"></canvas>
<script type="text/javascript">
(function() {
console.log("at top of script");
var ballImage = new Image();
ballImage.src = "http://upload.wikimedia.org/wikipedia/en/e/ec/Soccer_ball.svg";
var time_stamp;
var balls[];
var BALL_COUNT = 2;
function requestAnimFrame (cb) {
function fallback (cb){
window.setTimeout(cb, 10);
}
var func = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
fallback;
func (cb);
}
function update() {
var ts = Date.now();
var time_elapsed = ts - (time_stamp || ts);
time_stamp = ts;
canvas = document.getElementById("canvas");
// update ball positions and detect wall collisions
balls.forEach(function (ball){
canvas.clearRect( ball.xLocation, ball.yLocation, ball.scaleWidth, ball.scaleHeight );
ball.update(time_elapsed);
canvas.drawImage( ball.image, ball.xLocation, ball.yLocation, ball.scaleWidth, ball.scaleHeight );
});
}
function programRun() {
update();
requestAnimationFrame(programRun);
}
/*
CLASS: ball
*/
function Ball() {
// all the code and functions for the ball class....
}
ballImage.onload = function() {
// fill up the array containing the ball objects
for (var i = 0; i < BALL_COUNT; ++i){
balls.push (new Ball ("ball" + i));
}
// start running the program
programRun();
};
console.log("got to end of script");
}) ();
<script>
</body>
</html>
I'm almost certain it's some basic thing I messed up, because I didn't get any errors back from running the code. Any help is appreciated :)
This line caused it..
var balls[];
correct it by replacing it with:
var balls = [];
you should see the logs then.
Update:
I created a fiddle with the code above and revised some fragments of your code to conform to a version that may suit your needs in writing your own version of the ball animation.
The following are the problems that I saw in your code:
Of course the array declaration mentioned above var balls[]; to var balls = [];
Don't forget to declare variables with the var keyword. I changed
time_stamp = ts;
canvas = document.getElementById("canvas");
to
var time_stamp = ts;
var canvas = document.getElementById("canvas");
3. Your html code suggests that the ID of the canvas is id="console" but your javascript tries to get an element with id="canvas".
Changed
<canvas id="console" width="600" height="600"></canvas>
to
<canvas id="canvas" width="600" height="600"></canvas>
Inside your ball.each() iteration uses the canvas element to invoke methods that are supposed to be within the CanvasRenderingContext2D.
Add something after your var canvas declaration.
var ctx = canvas.getContext('2d');
use the ctx variable to manipulate the canvas itself. This should be your function update() definition.
function update() {
var ts = Date.now();
var time_elapsed = ts - (time_stamp || ts);
var time_stamp = ts;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
// update ball positions and detect wall collisions
balls.forEach(function (ball){
ctx.clearRect( ball.xLocation, ball.yLocation, ball.scaleWidth, ball.scaleHeight );
ball.update();
ctx.drawImage( ballImage, ball.xLocation, ball.yLocation, ball.scaleWidth, ball.scaleHeight );
});
}
UPDATE:
Always use var when declaring variables. If you want to use them as a variable that can be accessed from different scopes(inner scopes) then you can do it by declaring them a scope higher.
Something like this:
function BallAnimation() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
function updateBall() {
ctx.fillRect(.....);
...
}
function Ball() {
this.canvas = canvas;
}
}
In such a manner you can still obtain the reference of the variables that you want to share from other scopes. Read this article to know more why vars are important.

Cannot read property 'getContext' of null, using canvas

I get the error Uncaught TypeError: Cannot read property 'getContext' of null and the important parts in files are... I am wondering since game.js is in a directory below, it cannot find canvas? What should I do?
./index.html:
<canvas id="canvas" width="640" height="480"></canvas>
./javascript/game.js:
var Grid = function(width, height) {
...
this.draw = function() {
var canvas = document.getElementById("canvas");
if(canvas.getContext) {
var context = canvas.getContext("2d");
for(var i = 0; i < width; i++) {
for(var j = 0; j < height; j++) {
if(isLive(i, j)) {
context.fillStyle = "lightblue";
}
else {
context.fillStyle = "yellowgreen";
}
context.fillRect(i*15, j*15, 14, 14);
}
}
}
}
}
I guess the problem is your js runs before the html is loaded.
If you are using jquery, you can use the document ready function to wrap your code:
$(function() {
var Grid = function(width, height) {
// codes...
}
});
Or simply put your js after the <canvas>.
Put your JavaScript code after your tag <canvas></canvas>
You don't have to include JQuery.
In the index.html:
<canvas id="canvas" width="640" height="480"></canvas><script src="javascript/game.js">
This should work without JQuery...
Edit: You should put the script tag IN the body tag...
You should put javascript tag in your html file.
because browser load your webpage according to html flow, you should put your javascript file<script src="javascript/game.js"> after the <canvas>element tag. otherwise,if you put your javascript in the header of html.Browser load script first but it doesn't find the canvas. So your canvas doesn't work.
Write code in this manner ...
<canvas id="canvas" width="640" height="480"></canvas>
<script>
var Grid = function(width, height) {
...
this.draw = function() {
var canvas = document.getElementById("canvas");
if(canvas.getContext) {
var context = canvas.getContext("2d");
for(var i = 0; i < width; i++) {
for(var j = 0; j < height; j++) {
if(isLive(i, j)) {
context.fillStyle = "lightblue";
}
else {
context.fillStyle = "yellowgreen";
}
context.fillRect(i*15, j*15, 14, 14);
}
}
}
}
}
First write canvas tag and then write script tag. And write script tag in body.
You just need to put<script src='./javascript/game.js'></script> after your <canvas>.
Because the browser don't find your javascript file before the canvas
I assume you have your JS file declared inside the <head> tag so it keeps it consistent, like standard, then in your JS make sure the canvas initialization is after the page is loaded:
window.onload = function () {
var myCanvas = document.getElementById('canvas');
var ctx = myCanvas.getContext('2d');
}
There is no need to use jQuery just to initialize a canvas, it's very evident most of the programmers all around the world use it unnecessarily and the accepted answer is a probe of that.
This might seem like overkill, but if in another case you were trying to load a canvas from js (like I am doing), you could use a setInterval function and an if statement to constantly check if the canvas has loaded.
//set up the interval
var thisInterval = setInterval(function{
//this if statment checks if the id "thisCanvas" is linked to something
if(document.getElementById("thisCanvas") != null){
//do what you want
//clearInterval() will remove the interval if you have given your interval a name.
clearInterval(thisInterval)
}
//the 500 means that you will loop through this every 500 milliseconds (1/2 a second)
},500)
(In this example the canvas I am trying to load has an id of "thisCanvas")

How to make webkit free canvas context memory when canvas is destroyed

The code below seems to leak memory at a rather alarming rate on webkit (mobile safari & konqueror). I realize the test case could be rewritten to reuse the canvas instead of creating a new one, but I'm wondering why the below doesn't also work. Any insight would be appreciated.
<html>
<head>
<script>
function draw() {
var holder = document.getElementById("holder");
holder.innerHTML = "<canvas id=cnv height=250 width=250>";
var ctx = document.getElementById("cnv").getContext("2d");
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(Math.random()*100,Math.random()*100);
ctx.stroke();
}
function start() {
setInterval(draw, 100);
}
</script>
</head>
<body onload="start()">
<div id="holder"></div>
</body>
</html>
This issue happens on Webkit even when an image SRC is modified, so I don't get surprised if this happens when handling canvas too.
There is a bug filled on Chrome (that made them filling a bug to Webkit) and we hope this will be fixed soon, as it is making many Chrome extension unusable.
references
http://code.google.com/p/chromium/issues/detail?id=36142
https://bugs.webkit.org/show_bug.cgi?id=23372
Anyway, the suggestions above should mitigate this.
Don't:
Create the <canvas> element with .innerHTML
Create the <canvas> in each Interval
Do:
use var cv = document.createElement('canvas'); cv.setAttribute('height', '250'); // ...
cache the reference of cv at some init point and re-use that!!
<script>
var holder = document.getElementById("holder"),
var cv = document.createElement('canvas');
cv.setAttribute('id', 'cnv');
cv.setAttribute('height', '250');
cv.setAttribute('width', '250');
holder.appendChild(cv);
function draw() {
var ctx = cv.getContext("2d");
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(Math.random()*100,Math.random()*100);
ctx.stroke();
}
function start() {
setInterval(draw, 100);
}
</script>

Categories