Uncaught Reference Error: draw is not defined - javascript

I am trying to complete This tutorial on making a tic tac toe game using JavaScript and HTML5.
I've followed each step in the video multiple times. While my code seems to match the code in video I keep encountering an error: Uncaught Reference Error: draw is not defined. The error occurs in line 12.
I must be overlooking something. Can anyone point it out?
<!DOCTYPE html>
<html>
<head>
<title>Tic Tac Toe!</title>
<script type="text/javascript">
var c, canvas;
var turn = 1;
window.onload = function() {
canvas = document.getElementById("canvas");
c = canvas.getContext("2d");
draw();
}
var moves = [];
window.onclick = function(e) {
if(e.pageX < 500 && e.pageY < 500) {
var cX = Math.floor(e.pageX / (500 / 3));
var cY = Math.floor(e.pageY / (500 / 3));
var alreadyClicked = false;
for(i in moves) {
if(moves[i][0] == cX && moves[i][1] == cY) {
alreadyClicked = true;
}
}
if(alreadyClicked == false) {
moves[(moves.length)] = [cX, cY, turn];
turn = turn * -1;
draw();
}
}
var bg = new Image();
var x = new Image();
var o = new Image();
bg.src = "ttt_board.png";
x.src = "ttt_x.png";
o.src = "ttt_o.png";
function draw() {
c.clearRect(0, 0, 500, 500);
c.drawImage(bg, 0, 0);
for(i in moves) {
if(moves[i][2] == 1) {
c.drawImage(x, Math.floor(moves[i][0] * (500 / 3) + 10), Math.floor(moves[i][1](500 / 3) + 10))
} else {
c.drawImage(o, Math.floor(moves[i][0] * (500 / 3) + 10), Math.floor(moves[i][1] * (500 / 3) + 10));
}
}
}
};
</script>
</head>
<body>
<canvas id="canvas" width="500px" height="500px"></canvas>
</body>
</html>

The problem is that draw is defined in window.onclick, but you are trying to call it from window.onload. Are you sure you do not have a closing bracket missing before the definition of draw?

Try moving the draw() function out of the window.click function.
window.onclick = function(e) {
// ...
};
function draw() {
// ...
}
One of the major benefits of being strict with your indentation is that you can pick up these bugs very quickly.
window.click = function(e) {
// ...
function draw() {
// ...
}
};
Is the code you had.

As a result of draw being located inside of the window.onclick anonymous function, it is scoped there. Since window.onload does not share the scope of that anonymous function, you cannot access draw from window.onload. You should removed the draw function from your window.onclick event so that it may be called from other scopes.
function draw(){}
window.onload = function(){ /*code*/ };
window.onclick = function(){ /* code */ };

Related

P5js: why is my bg framerate not refreshing properly?

I have 2 sets of code in here. One is the simple bounce off code. The other is a function. I've tried to make it a function but it doesn't seem to be working properly.
The bg framerate doesn't clear in the sense that a string of balls show rather than a ball bouncing and animating.
if(this.y_pos > 400) this condition doesn't seem to be working even tho it works when it is drawn in the draw function.
var sketch = function (p) {
with(p) {
p.setup = function() {
createCanvas(800, 600);
// x_pos = 799;
// y_pos = 100;
// spdx = -random(5,10);
// spdy = random(12,17);
};
p.draw = function() {
background(0);
// fill(255);
// ellipse(x_pos,y_pos,50);
// x_pos += spdx;
// y_pos += spdy;
// if(y_pos > 400)
// {
// spdy *= -1;
// }
// for( var i = 0; i < 10; i++)
avalance(799, 100, random(5,10), random(12,17));
};
function avalance(x, y, spdx, spdy)
{
this.x_pos = x;
this.y_pos = y;
this.spdx = spdx;
this.spdy = spdy;
this.x_pos = 799;
this.y_pos = 100;
this.spdx = 1;
this.spdy = 1;
this.movement = function()
{
this.x_pos += -spdx;
this.y_pos += spdy;
if(this.y_pos > 400)
{
this.spdy *= -1;
}
}
this.draw = function()
{
this.movement();
this.drawnRox();
}
this.drawnRox = function()
{
fill(255);
ellipse(this.x_pos,this.y_pos,50);
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
Let's address both issues:
The draw() function is called for each new frame and in it you call avalance which creates a new ball. To fix that you need to
Create a global variable let ball; out of setup() and draw() so that you can reuse that later;
In setup create a new ball and assign it to your ball variable: ball = new avalance(799, 100, random(5,10), random(12,17));
In draw() you want to update the ball and that's what its own draw() function does (I would advise renaming it update() for example, to avoid confusion with the p5 specific draw() function). So you just need to call ball.draw() in draw().
This creates a ball which moves but still don't respect your 400px limit.
The issue is that in movement() you add spdx and spdy to the position but when the ball crosses the limit you update this.spdy, so you need to update the function with this code:
this.x_pos += -this.spdx;
this.y_pos += this.spdy;
And you should be good! Here is a code pend with your code working as you intend.
Also as a bonus advise: You probably want to use some p5.Vector objects to store positions, speeds and accelerations it really makes your code easier to read and to use. You could also rename your function Avalance (capital A) to show that you actually use a class and that this function shouldn't be called without new.
As #statox suggested, do new avalance(799, 100, random(5,10), random(12,17)) in the setup() section and call draw of the ball in draw() section.
You can test the code below by clicking "Run code snippet".
var sketch = function (p) {
with(p) {
var ball;
p.setup = function() {
createCanvas(800, 600);
ball = new avalance(799, 100, random(5,10), random(12,17));
};
p.draw = function() {
background(0);
ball.draw();
};
function avalance(x, y, spdx, spdy)
{
function movement ()
{
x += -spdx;
y += spdy;
if (y > height || y < 0)
{
spdy *= -1;
}
if (x > width || x < 0)
{
spdx *= -1;
}
}
this.draw = function()
{
movement();
drawnRox();
}
function drawnRox ()
{
fill(255);
ellipse(x,y,50);
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>

Javascript canvas "game". Hero mooves only one time

Im trying to make simple game in canvas. I made animation for hero using setTimeout() function. I check pressed keys with function moove(e):
Everything works pretty fine when i press leftarrow or rightarrow for the first time, but then hero doesnt moove. Any recomendations to the code is appreciated.
var cns = document.getElementById("can");
cns.height = 600;
cns.width = 300;
var ctx = cns.getContext("2d");
var hero = new Image();
hero.src = "images/hero.png";
hero.onload = function() {
ctx.drawImage(hero, 120, 570);
hero.xx = 120;
hero.yy = 570;
};
var intervalL, intervalR, intervalLL, intervalRR;
var keys = [];
function moove(e) {
keys[e.keyCode] = (e.type == "keydown");
if (keys[37]) {
clearTimeout(intervalR);
clearTimeout(intervalRR);
goLeft(hero);
} else {
clearTimeout(intervalL);
clearTimeout(intervalLL);
}
if (keys[39]) {
clearTimeout(intervalL);
clearTimeout(intervalLL);
goRight(hero);
} else {
clearTimeout(intervalR);
clearTimeout(intervalRR);
}
}
function goLeft(img) {
var x = img.xx,
y = img.yy;
function f() {
ctx.clearRect(img.xx, img.yy, img.width, img.height);
ctx.drawImage(img, x, y);
img.xx = x;
img.yy = y;
x -= 1.2;
if (x < -35) {
x = cns.width;
}
}
if (!intervalL) {
intervalL = setTimeout(function run() {
f();
intervalLL = setTimeout(run, 5);
}, 5);
}
}
Function goRight is similiar to goLeft.
Function moove is called in tag body onkeydown='moove(event)' onkeyup='moove(event)'.
You can check the project here: https://github.com/Fabulotus/Fabu/tree/master/Canvas%20game%20-%20dodge%20and%20jump
The reason it doesn't work the first time is because the first time through you are setting the position to its previous position (x = image.xx) then updating x after you draw. You should update the x value x -= 1.2 before calling drawImage
Here is a "working" version of your code:
var cns = document.getElementById("can");
cns.height = 170;
cns.width = 600;
var ctx = cns.getContext("2d");
var hero = new Image();
hero.src = "http://swagger-net-test.azurewebsites.net/api/Image";
hero.onload = function() {
ctx.drawImage(hero, cns.width-10, cns.height/2);
hero.xx = cns.width-10;
hero.yy = cns.height/2;
};
var intervalL, intervalR, intervalLL, intervalRR;
var keys = [];
function goLeft(img) {
function f() {
ctx.beginPath()
ctx.clearRect(0, 0, cns.width, cns.height);
ctx.drawImage(img, img.xx, img.yy);
img.xx--;
if (img.xx < -img.width) {
img.xx = cns.width;
}
}
if (!intervalL) {
intervalL = setTimeout(function run() {
f();
intervalLL = setTimeout(run, 5);
}, 5);
}
}
goLeft(hero)
<canvas id="can">
As you can see the function goLeft has been significantly simplified.
One recommendation: avoid the many setTimeout and clearTimeout instead use one setInterval to call a draw function that takes care of drawing everything on your game, all the other function should just update the position of your gameObjects.

Javascript Key Pressed + setTimeout()

I am making a game with html and javascript. Just for clarification, this is not a duplicate or anything. Nothing has the answer I need. Also before I explain, I want to say I have no trouble with the key listener, my game knows when a key is pressed and when it is released. Okay, I have 5 frames of a character walking. I have a while loop that basically says while the key D is pressed or Right arrow is pressed, then increment the frames to make it look like the character is walking. Then it has a setTimeout function that pauses for 1/10 of a second. This should make it look like the character is walking. I know it has something to do with the setTimeout() function. Here is the while loop:
while (keys[68] || keys[39]) {
charFrame++;
setTimeout(function() {
}, 100);
}
Then here is my code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sparring Spartans</title>
<style type="text/css">
</style>
</head>
<body>
<canvas id="canvas" width="1000" height="600"></canvas>
<script type="text/javascript">
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
var groundX = 0, groundY = 400;
var playerx = width/2, playery = groundY-120;
var charFrame = 1;
var speed = 4;
var keys = [];
window.addEventListener("keydown", function(e) {
keys[e.keyCode] = true;
}, false);
window.addEventListener("keyup", function(e) {
delete keys[e.keyCode];
}, false);
var spartan1 = new Image();
spartan1.src = "spartan1.png";
var spartan2 = new Image();
spartan2.src = "spartan2.png";
var spartan3 = new Image();
spartan3.src = "spartan3.png";
var spartan4 = new Image();
spartan4.src = "spartan4.png";
var spartan5 = new Image();
spartan5.src = "spartan5.png";
var stone = new Image();
stone.src = "stone.png";
function game() {
update();
render();
}
function player() {
if (charFrame === 1) {
context.drawImage(spartan1, playerx, playery);
} else if (charFrame === 2) {
context.drawImage(spartan2, playerx, playery);
} else if (charFrame === 3) {
context.drawImage(spartan3, playerx, playery);
} else if (charFrame === 4) {
context.drawImage(spartan4, playerx, playery);
} else if (charFrame === 5) {
context.drawImage(spartan5, playerx, playery);
}
}
function ground() {
for (var i = 0; i <= 1000; i += 55) {
context.drawImage(stone, i, groundY);
}
for (var i = 0; i <= 1000; i += 55) {
context.drawImage(stone, i, groundY+55);
}
for (var i = 0; i <= 1000; i += 55) {
context.drawImage(stone, i, groundY+110);
}
for (var i = 0; i <= 1000; i += 55) {
context.drawImage(stone, i, groundY+165);
}
}
function manager() {
ground();
while (keys[68] || keys[39]) {
charFrame++;
setTimeout(function() {
}, 100);
}
player();
}
function update() {
manager();
player();
}
function render() {
context.fillStyle = "#AFE4FF";
context.fillRect(0, 0, width, height);
manager();
}
setInterval(function() {
game();
}, 1000/30);
</script>
</body>
</html>
What am I doing wrong? I know it HAS to be the setTimeout, but I have tried to fix this problem many times. The images don't even change.
Because your manager function gets called 30 times per second, you should check your keypress once per frame using an if statement instead of a loop:
function manager() {
ground();
if (keys[68] || keys[39]) {
charFrame++;
}
}
In addition, your manager function is getting called twice per frame, once in the update function and once in the render function. Also, your player function is called both in update and in manager, again twice per frame. Your code will need some refactoring in order to work properly, to make sure nothing gets called twice per frame.

Why does chrome struggle to display lots of images on a canvas when the other browsers don't?

We're working with the HTML5 canvas, displaying lots of images at one time.
This is working pretty well but recently we've had a problem with chrome.
When drawing images on to a canvas you seem to reach a certain point where the performance degrades very quickly.
It's not a slow effect, it seems that you go right from 60fps to 2-4fps.
Here's some reproduction code:
// Helpers
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/random
function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
// http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
window.requestAnimFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); }; })();
// https://github.com/mrdoob/stats.js
var Stats = function () { var e = Date.now(), t = e; var n = 0, r = Infinity, i = 0; var s = 0, o = Infinity, u = 0; var a = 0, f = 0; var l = document.createElement("div"); l.id = "stats"; l.addEventListener("mousedown", function (e) { e.preventDefault(); y(++f % 2) }, false); l.style.cssText = "width:80px;opacity:0.9;cursor:pointer"; var c = document.createElement("div"); c.id = "fps"; c.style.cssText = "padding:0 0 3px 3px;text-align:left;background-color:#002"; l.appendChild(c); var h = document.createElement("div"); h.id = "fpsText"; h.style.cssText = "color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; h.innerHTML = "FPS"; c.appendChild(h); var p = document.createElement("div"); p.id = "fpsGraph"; p.style.cssText = "position:relative;width:74px;height:30px;background-color:#0ff"; c.appendChild(p); while (p.children.length < 74) { var d = document.createElement("span"); d.style.cssText = "width:1px;height:30px;float:left;background-color:#113"; p.appendChild(d) } var v = document.createElement("div"); v.id = "ms"; v.style.cssText = "padding:0 0 3px 3px;text-align:left;background-color:#020;display:none"; l.appendChild(v); var m = document.createElement("div"); m.id = "msText"; m.style.cssText = "color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; m.innerHTML = "MS"; v.appendChild(m); var g = document.createElement("div"); g.id = "msGraph"; g.style.cssText = "position:relative;width:74px;height:30px;background-color:#0f0"; v.appendChild(g); while (g.children.length < 74) { var d = document.createElement("span"); d.style.cssText = "width:1px;height:30px;float:left;background-color:#131"; g.appendChild(d) } var y = function (e) { f = e; switch (f) { case 0: c.style.display = "block"; v.style.display = "none"; break; case 1: c.style.display = "none"; v.style.display = "block"; break } }; var b = function (e, t) { var n = e.appendChild(e.firstChild); n.style.height = t + "px" }; return { REVISION: 11, domElement: l, setMode: y, begin: function () { e = Date.now() }, end: function () { var f = Date.now(); n = f - e; r = Math.min(r, n); i = Math.max(i, n); m.textContent = n + " MS (" + r + "-" + i + ")"; b(g, Math.min(30, 30 - n / 200 * 30)); a++; if (f > t + 1e3) { s = Math.round(a * 1e3 / (f - t)); o = Math.min(o, s); u = Math.max(u, s); h.textContent = s + " FPS (" + o + "-" + u + ")"; b(p, Math.min(30, 30 - s / 100 * 30)); t = f; a = 0 } return f }, update: function () { e = this.end() } } }
// Firefox events suck
function getOffsetXY(eventArgs) { return { X: eventArgs.offsetX == undefined ? eventArgs.layerX : eventArgs.offsetX, Y: eventArgs.offsetY == undefined ? eventArgs.layerY : eventArgs.offsetY }; }
function getWheelDelta(eventArgs) { if (!eventArgs) eventArgs = event; var w = eventArgs.wheelDelta; var d = eventArgs.detail; if (d) { if (w) { return w / d / 40 * d > 0 ? 1 : -1; } else { return -d / 3; } } else { return w / 120; } }
// Reproduction Code
var stats = new Stats();
document.body.appendChild(stats.domElement);
var masterCanvas = document.getElementById('canvas');
var masterContext = masterCanvas.getContext('2d');
var viewOffsetX = 0;
var viewOffsetY = 0;
var viewScaleFactor = 1;
var viewMinScaleFactor = 0.1;
var viewMaxScaleFactor = 10;
var mouseWheelSensitivity = 10; //Fudge Factor
var isMouseDown = false;
var lastMouseCoords = null;
var imageDimensionPixelCount = 25;
var paddingPixelCount = 2;
var canvasDimensionImageCount = 50;
var totalImageCount = Math.pow(canvasDimensionImageCount, 2);
var images = null;
function init() {
images = createLocalImages(totalImageCount, imageDimensionPixelCount);
initInteraction();
renderLoop();
}
function initInteraction() {
var handleMouseDown = function (eventArgs) {
isMouseDown = true;
var offsetXY = getOffsetXY(eventArgs);
lastMouseCoords = [
offsetXY.X,
offsetXY.Y
];
};
var handleMouseUp = function (eventArgs) {
isMouseDown = false;
lastMouseCoords = null;
}
var handleMouseMove = function (eventArgs) {
if (isMouseDown) {
var offsetXY = getOffsetXY(eventArgs);
var panX = offsetXY.X - lastMouseCoords[0];
var panY = offsetXY.Y - lastMouseCoords[1];
pan(panX, panY);
lastMouseCoords = [
offsetXY.X,
offsetXY.Y
];
}
};
var handleMouseWheel = function (eventArgs) {
var mouseX = eventArgs.pageX - masterCanvas.offsetLeft;
var mouseY = eventArgs.pageY - masterCanvas.offsetTop;
var zoom = 1 + (getWheelDelta(eventArgs) / mouseWheelSensitivity);
zoomAboutPoint(mouseX, mouseY, zoom);
if (eventArgs.preventDefault !== undefined) {
eventArgs.preventDefault();
} else {
return false;
}
}
masterCanvas.addEventListener("mousedown", handleMouseDown, false);
masterCanvas.addEventListener("mouseup", handleMouseUp, false);
masterCanvas.addEventListener("mousemove", handleMouseMove, false);
masterCanvas.addEventListener("mousewheel", handleMouseWheel, false);
masterCanvas.addEventListener("DOMMouseScroll", handleMouseWheel, false);
}
function pan(panX, panY) {
masterContext.translate(panX / viewScaleFactor, panY / viewScaleFactor);
viewOffsetX -= panX / viewScaleFactor;
viewOffsetY -= panY / viewScaleFactor;
}
function zoomAboutPoint(zoomX, zoomY, zoomFactor) {
var newCanvasScale = viewScaleFactor * zoomFactor;
if (newCanvasScale < viewMinScaleFactor) {
zoomFactor = viewMinScaleFactor / viewScaleFactor;
} else if (newCanvasScale > viewMaxScaleFactor) {
zoomFactor = viewMaxScaleFactor / viewScaleFactor;
}
masterContext.translate(viewOffsetX, viewOffsetY);
masterContext.scale(zoomFactor, zoomFactor);
viewOffsetX = ((zoomX / viewScaleFactor) + viewOffsetX) - (zoomX / (viewScaleFactor * zoomFactor));
viewOffsetY = ((zoomY / viewScaleFactor) + viewOffsetY) - (zoomY / (viewScaleFactor * zoomFactor));
viewScaleFactor *= zoomFactor;
masterContext.translate(-viewOffsetX, -viewOffsetY);
}
function renderLoop() {
clearCanvas();
renderCanvas();
stats.update();
requestAnimFrame(renderLoop);
}
function clearCanvas() {
masterContext.clearRect(viewOffsetX, viewOffsetY, masterCanvas.width / viewScaleFactor, masterCanvas.height / viewScaleFactor);
}
function renderCanvas() {
for (var imageY = 0; imageY < canvasDimensionImageCount; imageY++) {
for (var imageX = 0; imageX < canvasDimensionImageCount; imageX++) {
var x = imageX * (imageDimensionPixelCount + paddingPixelCount);
var y = imageY * (imageDimensionPixelCount + paddingPixelCount);
var imageIndex = (imageY * canvasDimensionImageCount) + imageX;
var image = images[imageIndex];
masterContext.drawImage(image, x, y, imageDimensionPixelCount, imageDimensionPixelCount);
}
}
}
function createLocalImages(imageCount, imageDimension) {
var tempCanvas = document.createElement('canvas');
tempCanvas.width = imageDimension;
tempCanvas.height = imageDimension;
var tempContext = tempCanvas.getContext('2d');
var images = new Array();
for (var imageIndex = 0; imageIndex < imageCount; imageIndex++) {
tempContext.clearRect(0, 0, imageDimension, imageDimension);
tempContext.fillStyle = "rgb(" + getRandomInt(0, 255) + ", " + getRandomInt(0, 255) + ", " + getRandomInt(0, 255) + ")";
tempContext.fillRect(0, 0, imageDimension, imageDimension);
var image = new Image();
image.src = tempCanvas.toDataURL('image/png');
images.push(image);
}
return images;
}
// Get this party started
init();
And a jsfiddle link for your interactive pleasure:
http://jsfiddle.net/BtyL6/14/
This is drawing 50px x 50px images in a 50 x 50 (2500) grid on the canvas. I've also quickly tried with 25px x 25px and 50 x 50 (2500) images.
We have other local examples that deal with bigger images and larger numbers of images and the other browser start to struggle with these at higher values.
As a quick test I jacked up the code in the js fiddle to 100px x 100px and 100 x 100 (10000) images and that was still running at 16fps when fully zoomed out. (Note: I had to lower the viewMinScaleFactor to 0.01 to fit it all in when zoomed out.)
Chrome on the other hand seems to hit some kind of limit and the FPS drops from 60 to 2-4.
Here's some info about what we've tried and the results:
We've tried using setinterval rather than requestAnimationFrame.
If you load 10 images and draw them 250 times each rather than 2500 images drawn once each then the problem goes away. This seems to indicate that chrome is hitting some kind of limit/trigger as to how much data it's storing about the rendering.
We have culling (not rendering images outside of the visual range) in our more complex examples and while this helps it's not a solution as we need to be able to show all the images at once.
We have the images only being rendered if there have been changes in our local code, against this helps (when nothing changes, obviously) but it isn't a full solution because the canvas should be interactive.
In the example code we're creating the images using a canvas, but the code can also be run hitting a web service to provide the images and the same behaviour (slowness) will be seen.
We've found it very hard to even search for this issue, most results are from a couple of years ago and woefully out of date.
If any more information would be useful then please ask!
EDIT: Changed js fiddle URL to reflect the same code as in the question. The code itself didn't actually change, just the formatting. But I want to be consistent.
EDIT: Updated jsfiddle and and code with css to prevent selection and call requestAnim after the render loop is done.
In Canary this code freezes it on my computer. As to why this happens in Chrome the simple answer is that it uses a different implementation than f.ex. FF. In-depth detail I don't know, but there is obviously room for optimizing the implementation in this area.
I can give some tip however on how you can optimize the given code to make it run in Chrome as well :-)
There are several things here:
You are storing each block of colors as images. This seem to have a huge performance impact on Canary / Chrome.
You are calling requestAnimationFrame at the beginning of the loop
You are clearing and rendering even if there are no changes
Try to (addressing the points):
If you only need solid blocks of colors, draw them directly using fillRect() instead and keep the color indexes in an array (instead of images). Even if you draw them to an off-screen canvas you will only have to do one draw to main canvas instead of multiple image draw operations.
Move requestAnimationFrame to the end of the code block to avoid stacking.
Use dirty flag to prevent unnecessary rendering:
I modified the code a bit - I modified it to use solid colors to demonstrate where the performance impact is in Chrome / Canary.
I set a dirty flag in global scope as true (to render the initial scene) which is set to true each time the mouse move occur:
//global
var isDirty = true;
//mouse move handler
var handleMouseMove = function (eventArgs) {
// other code
isDirty = true;
// other code
};
//render loop
function renderLoop() {
if (isDirty) {
clearCanvas();
renderCanvas();
}
stats.update();
requestAnimFrame(renderLoop);
}
//in renderCanvas at the end:
function renderCanvas() {
// other code
isDirty = false;
}
You will of course need to check for caveats for the isDirty flag elsewhere and also introduce more criteria if it's cleared at the wrong moment. I would store the old position of the mouse and only (in the mouse move) if it changed set the dirty flag - I didn't modify this part though.
As you can see you will be able to run this in Chrome and in FF at a higher FPS.
I also assume (I didn't test) that you can optimize the clearCanvas() function by only drawing the padding/gaps instead of clearing the whole canvas. But that need to be tested.
Added a CSS-rule to prevent the canvas to be selected when using the mouse:
For further optimizing in cases such as this, which is event driven, you don't actually need an animation loop at all. You can just call the redraw when the coords or mouse-wheel changes.
Modification:
http://jsfiddle.net/BtyL6/10/
This was a legitimate bug in chrome.
https://code.google.com/p/chromium/issues/detail?id=247912
It has now been fixed and should be in a chrome mainline release soon.

I need help finding errors in my JavaScript mini-game (Three error that i don't get)

This is my first time making a mini-game from complete scratch.
Google chrome gives me these errors at runtime:
Uncaught TypeError: Cannot call method 'draw' of undefined Logic.js:28
loop Logic.js:28
startLoop Logic.js:35
init Logic.js:19
It was working fine when i used "setInterval" but i want the newest stuff.
I don't think that requestAnimationFrame has anything to do with it.
But i don't see what's wrong! Please help.
// Create the canvas
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
canvas.addEventListener('click', canvasClick, false);
//Declarations
var isPlaying = false;
var animeFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame;
var Pair1;
var Pair2;
//init
function init(){
startLoop();
Pair1 = new Pair();
Pair2 = new Pair();
alert('init called');
}
//Draw
function loop(){
if(isPlaying){
Pair1.draw();
Pair2.draw();
animeFrame(loop);
}
}
function startLoop(){
isPlaying = true;
loop();
}
function stopLoop(){
isPlaying = false;
}
//Interface
function canvasClick(){
var X = event.x;
var Y = event.y;
X -= canvas.offsetLeft;
Y -= canvas.offsetTop;
if(X > Pair1.X && X < Pair1.X + 64){
if(Y > Pair1.Y && Y < Pair1.Y + 96){
alert('Clicked Pair1');
};
};
}
//Create Images
var pairImg = new Image();
pairImg.src = "images/Gold_Pair.png";
pairImg.addEventListener('load',init,false)
//Pair
function Pair(){
var X = Math.floor(Math.random() * (canvas.width - 64));
var Y = Math.floor(Math.random() * (canvas.height - 96));
}
//Draw
Pair.prototype.draw = function(){
ctx.drawImage(pairImg, this.X, this.Y, 64, 96);
};
Thanks for you replys!!!
Your "init" function calls "startLoop", which calls "loop", which expects that "Pair1" and "Pair2" have been initialized. However, "init" doesn't initialize them until after calling "startLoop".
Try changing "init":
function init(){
Pair1 = new Pair();
Pair2 = new Pair();
startLoop();
alert('init called');
}
I think the problem is that your function loop requires Pair1 and Pair2 to exist, but init doesn’t do that until after loop has been called (via startloop).
Maybe this version of init would work?
//init
function init(){
Pair1 = new Pair();
Pair2 = new Pair();
startLoop();
alert('init called');
}

Categories