Javascript arrays giving NaN - javascript

I have a bit of HTML that is supposed to bounce ball(s) around the canvas, but the arrays storing the coordinates seem to be 'NaN' when I set them to a random position and test with alert("Co-ordinates " + (cirX[i]) + " x " + (cirY[i]));. This returns 'Co-ordinates NaN x NaN'. I have tried to do it with one ball without the arrays and it worked. I am not sure if I am coding my arrays badly or if it is something else. Here is my HTML:
<!Doctype HTML>
<head>
<script>
var cirX = [];
var cirY = [];
var chX = [];
var chY = [];
var width;
var height;
function initCircle(nBalls) {
alert(nBalls)
for(var i = 0; i<nBalls;i++) {
alert("loop " + i)
chX[i] = (Math.floor(Math.random()*200)/10);
chY[i] = (Math.floor(Math.random()*200)/10);
cirX[i] = Math.floor(Math.random()*width);
cirY[i] = Math.floor(Math.random()*height);
alert("Co-ordinates " + (cirX[i]) + " x " + (cirY[i]));
circle(cirX[i],cirY[i],3);
setInterval('moveBall(i)',10);
}
}
function moveBall(ballNum) {
if(cirX[ballNum] > width||cirX[ballNum] < 0) {
chX[ballNum] = 0-chX[ballNum];
}
if(cirY[ballNum] > height|| cirY[ballNum] < 0) {
chY[ballNum] = 0-chY[ballNum];
}
cirX[ballNum] = cirX[ballNum] + chX[ballNum];
cirY[ballNum] = cirY[ballNum] + chY[ballNum];
circle(cirX[ballNum],cirY[ballNum],3);
}
function circle(x,y,r) {
var c=document.getElementById("canvas");
var ctx=c.getContext("2d");
canvas.width = canvas.width;
ctx.fillStyle="#FF0000";
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI*2, true);
ctx.fill();
width = canvas.width;
height = canvas.height;
}
</script>
</head>
<body>
<canvas id="canvas" width="400" height="300">
</canvas>
<script>
initCircle(3); //this sets the number of circles
</script>
</body>
I have looked up how to initialise arrays e.c.t, but I seem to be doing it right? Thanks in advance for your help!
EDIT:
Despite fixing the above problems, only one ball moves and at varying speeds despite the variable ballNum in moveBall() varying from 0 to 2 as expected (tested by adding alert(ballNum)). Does anyone know why?

You call this line
cirX[i] = Math.floor(Math.random()*width);
when width is still undefined. So you can only get NaN as a result.
To properly call the moveBall function from setInterval, you may use
(function(i) { // embedds i to protect its value (so that it isn't the one of end of loop
setInterval(function(){moveBall(i)}, 10);
})(i);

It is because width is undefined when the statement gets executed for the first time. You can get the canvas and its dimension in the beginning and keep it global.
http://jsbin.com/agavoq/9/edit
To call setInterval you can use a self invoking function preserving the value of i
(function(x){
setInterval(moveBall,10, x);
})(i);
Or simply
setInterval(moveBall,10, i);
Or
setInterval('moveBall(' + i+ ')',10);

I see another issue because look at this line
setInterval('moveBall(i)',10);
The i is not what you think it is. You should not use a string in setTimeout.

Related

how to detect an element inside a canvas?

I am trying to detect the drawn elements inside the canvas (for example: every slice in the pie) to put a click event on them later, but it seems not to be an easy task, I think the math would be the best way to carry it out, but where should I start.
Please Help me to find a solution for this.
here is the demo:
outer link
code is:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#myCanvas{
width: 50%;
height: 50%;
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
</body>
</html>
class Piechart{
constructor(){
this.options = arguments[0];
this.canvas = arguments[0].canvas;
this.ctx = this.canvas.getContext('2d');
if(this.options.colors){
this.colors = arguments[0].colors;
}
else if (!this.options.colors){
this.colors = [
"#fde23e",
"#f16e23",
"#57d9ff",
"#937e88",
'#2fe678',
'#228888',
'#b111c1'
];
}
this.donut_hole = 0.5;
}
drawPie(){
var total_value = 0;
var color_index = 0;
for(var categ in this.options.data){
var val = this.options.data[categ];
total_value += val;
}
var start_angle = 0;
for(var categ in this.options.data){
var val = this.options.data[categ];
var slice_angle = 2*Math.PI*val/total_value;
this.drawPieSlice(
this.ctx,
this.canvas.width/2,
this.canvas.height/2,
Math.min(this.canvas.width/2,this.canvas.height/2),
start_angle,
start_angle+slice_angle,
this.colors[color_index%this.colors.length]
);
start_angle += slice_angle;
color_index++;
}
}
drawPieSlice(){
arguments[0].fillStyle = arguments[6];
arguments[0].beginPath();
arguments[0].moveTo(arguments[1],arguments[2]);
arguments[0].arc(
arguments[1],
arguments[2],
arguments[3],
arguments[4],
arguments[5]
);
arguments[0].closePath();
arguments[0].fill();
}
}
var myCanvas = document.getElementById("myCanvas");
var myCanvas_width = myCanvas.scrollWidth;
var myCanvas_height = myCanvas.scrollHeight;
myCanvas.width = myCanvas_width;
myCanvas.height = myCanvas_height;
var myVinyls = {
'Classical music':10,
'Rock':14,
'Pop':15,
'Jazz':4,
'test':6,
'test_1':7,
'test_2':8
};
var myPiechart = new Piechart(
{
canvas:myCanvas,
data:myVinyls,
colors:[
"#fde23e",
"#f16e23",
"#57d9ff",
"#937e88",
'#2fe678',
'#228888',
'#b111c1'
]
}
);
myPiechart.drawPie();
How to be able to detect each slice? Thank you.
EDIT
I have found an amazing solution for the purpose of adding hit region in the canvas and here is the link: Anton Lavrenov
First of all there are no elements on canvas you should understand this. It is a 2d plane where things are just drawn and if you want to move things around you need to redraw them entirely.
This said lets go back to the original problem.
Since you create the "object" in the canvas you know the size and distances of the object which you should keep in some js object for later use.
what i would do in this case to have an object that will hold the data of all other objects so when you click on the canvas you can get the click x and y and determine whether they are greater or equal to any of your objects' xs and ys
an example algorithm would be
if click.X>elem.X && click.X<elem.X+elem.Width
{
x is in check the same for y
}
if x__in&&y__in
{
you clicked the element
}

Loading Image in P5.js

Hey I hope somebody can help me out with this question, well basically I am trying to upload a picture in my canvas through the p5.js library. And as soon as I load my html file I get this:
I am using brackets, I already tried to set up a web server with wamp server but same thing happened. Here is the code:
//HomePage.js
var rect = [];
var rooms = [];
function preload() {
for (var i = 0; i <= 1; i++){
rooms[i] = loadImage("Raeume/" + i + ".jpg");
}
}
function setup() {
cnv = createCanvas(1000, 600);
}
function mousePressed() {
var r = floor(random(0, rooms.length));
var b = new Raeume(mouseX, mouseY, rooms[r]);
rooms.push(b);
}
function draw() {
background(0);
for (var i = rooms.length-1; i >= 0; i--) {
rooms[i].update();
rooms[i].display();
}
}
//Raeume.js
function Room (x, y, img) {
this.x = x;
this.y = y;
this.img = img;
this.display = function () {
imageMode(CENTER);
image(img, this.x, this.y, 100, 100);
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Raum 007</h1>
<script src="p5/p5.min.js"></script>
<script src="HomePage.js"></script>
<script src="Raeume.js"></script>
</body>
</html>
I really hope you guys can help me on this one! Thanks!
Well, for one, your rooms array is holding first two images (assuming those are the right urls), and then you also add a Raeume object onto the end on mousePressed. Thus, when you call update and display on each entity in the array, it might throw some errors. It's very hard to tell what's the real problem since you don't have an example, but I would say the most likely problem is that your images aren't actually named 0.jpg and 1.jpg. If that's not the prob, then you could try running it in firefox, as I think it's more friendly with p5.js.

Div positioning calculation explanation required

I have attached the screenshot below to explain what i am trying to do.
The yellow highlighted line is the script which is run to get the position of the div (The red box in the picture).
I have used this code to calculate the position.
function getPosition(element) {
var xPosition = 0;
var yPosition = 0;
var left = 0;
var top = 0;
var i = 0;
while (element) {
xPosition = (element.offsetLeft);
yPosition = (element.offsetTop);
console.log("TOP Pos: "+yPosition+"Left Pos: "+xPosition);
if (i == 1) {
left = xPosition;
top = yPosition;
}
element = element.offsetParent;
i++;
}
return {
x: left,
y: top
};
}
And here i have used this method
function ReadDivPos(selector) {
var _divPos = "";
var parentDoc = window;
while (parentDoc !== parentDoc.parent) {
parentDoc = parentDoc.parent;
}
parentDoc = parentDoc.document;
var parentDiv = parentDoc.getElementsByTagName('div');
var divs = [];
for (var i = 0; i < parentDiv.length; i++) {
if (parentDiv[i].className == "content") {
var pos = getPosition(parentDiv[i]);
var x = pos["x"];
var y = pos["y"];
console.log("Values+ Top: " + y + " Left: " + x);
var w = parentDiv[i].offsetWidth;
_divPos += x + "," + w + "," + y + "," + (x + w) + ","+window.screen.availWidth+"\\n";
}
}
console.log("Values+ x: " + _divPos);
return _divPos;
}
Interestingly i am getting three values and on the second attempt i am getting the correct values. Here is the screenshot showing all the three values.
The correct value is
TOP Pos: 185Left Pos: 197
which i got it in the second attempt. Can anyone explain me why i did not get the correct values in the first attempt or is there any efficient way to get these values. I have to get the parent node because this was the only way to access the div class='content' as script is placed before the div content so i have to read the parent nodes and then i am able to access the required div.
Please Note this is the copy of my original question(Div Positioning is calculated fine but need explanation how it is working). The guy asked me to accept his answer and then he will show how it is done but he never came back to me once i accepted his answer and unfortunately i have also forgot my userid so i am able to logon to my orignal account.
If someone just explain me why this is giving me correct positions in the second attempt. I am new to frontend development if i understand this concept then it will help in my future projects. Thanks in advance

Not Looping? HTML5 and JavaScript

I have no idea why this code does not loop as it should. My mind is blown and hopefully someone can give me a hand. This is my first attempt into the HTML5 and JavaScript world and my first StackOverflow post. My background is in java so that should explain the quirks in my code. By the way, if you run the code the canvas and balls will show up, just not move.
First off, here is the HTML5
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>ChainReaction5</title>
<script type="text/javascript" src="chain_reaction.js"></script>
</head>
<body>
<body onLoad="init();">
<canvas id="myCanvas" width="500" height="400">
Your browser dosen't support the HTML5 canvas.</canvas><br />
</body>
</html>
Secondly here is the js
//gobal vars
var context;
var box;
var balls;
var defaultBallX=240;
var defaultBallY=190;
var defaultBallRad=6;
var defaultBallV=5;
var defaultNumBalls=10;
//box class
function Box() {
var boxx=20;
var boxy=20;
var boxWidth=460;
var boxHeight=360;
this.getX = function() {return boxx;}
this.getY = function() {return boxy;}
this.getWidth = function() {return boxWidth;}
this.getHeight = function() {return boxHeight;}
this.getBalls = function() {return ball;}
this.paintMe = function() {
context.fillStyle = "black";
context.strokeRect(boxx, boxy, boxWidth, boxHeight);
}
}
/* Box Class
* this class is sloppy but more memory efficent
*/
function Ball(x, y, radius, vx, vy, color) {
this.x=x;
this.y=y;
this.radius=radius;
this.vx=vx;
this.vy=vy;
this.color=color;
this.paintMe = function() {
context.beginPath();
context.arc(this.x, this.y, radius, 0, 2*Math.PI, true);
context.fillStyle = this.color;
context.fill();
}
}
Array.prototype.appendBalls = new function(array) {}
Array.prototype.clearBalls = new function() {}
Array.prototype.appendBalls = function(array) {
for (var i = 0; i < array.length; i++) {
balls.push(array[i]);
}
}
Array.prototype.clearBalls = function() {
balls = new Array();
}
// begin program
function init() {
context = document.getElementById("myCanvas").getContext("2d");
box = new Box();
balls = new Array();
balls.appendBalls(createBalls(box, defaultNumBalls));
setInterval(moveBall(balls, box), 100);
}
function createBalls(box, numBalls) {
var locBalls = new Array(numBalls);
for (var i = 0; i < numBalls; i++) {
var randx = randp(50, 400)
var randy = randp(50, 300);
var randr = Math.random()*defaultBallRad+1;
var randvx = randv();
var randvy = randv();
var randc = randColor();
locBalls[i] = new Ball(randx, randy, randr, randvx, randvy, randc);
}
return locBalls;
function randv() {
var neg = 1;
if (Math.random()>.5) neg = -neg;
return Math.random()*defaultBallV*neg;
}
function randp(low, hight) {
if (low < 0) low = 0;
var p = -1;
while (p > hight || p < low) {
p = Math.random()*hight;
}
return p;
}
function randColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.round(Math.random() * 15)];
}
return color;
}
}
function moveBall(balls, box) {
clear(this.box);
this.box.paintMe();
for (var i = 0; i < this.balls.length; i++) {
moveAndCheck(this.balls[i], this.box);
}
}
function moveAndCheck(b, box) {
if ((b.x+b.vx+b.radius-1)>(this.box.boxWidth+this.box.boxx) || b.x+b.vx-b.radius<this.box.boxx+1) {
b.vx = -b.vx;
}
if ((b.y+b.vy+b.radius-1)>(this.box.boxHeight+this.box.boxy) || b.y+b.vy-b.radius<this.box.boxy+1) {
b.vy = -b.vy;
}
b.x += b.vx;
b.y += b.vy;
b.paintMe();
}
function clear(box) {
context.clearRect(this.box.boxx, this.box.boxy,
this.box.boxWidth, this.box.boxHeight);
}
The first time I tried running it I got the following in the Firebug console:
useless setInterval call (missing quotes around argument?)
[Break On This Error] setInterval(moveBall(balls, box), 100);
Putting quotes around 'moveBalls(balls, box)' animates things.
Incidentally, you can use prototype inhertiance to make your function a bit more efficient, the methods in Box are given to each instance. To have them inherit the methods, put them on the constructor's prototype:
Box.prototype = {
getX: function() {return this.boxx;},
getY: function() {return this.boxy;},
getWidth: function() {return this.boxWidth;},
getHeight: function() {return this.boxHeight;},
getBalls: function() {return this.ball;},
paintMe: function() {
context.fillStyle = "black";
context.strokeRect(this.boxx, this.boxy, this.boxWidth, this.boxHeight);
}
};
Note that in javascript, a function's this keyword is set by the call, it is not set by how you declare the function (though you can use ES5 bind, but that is not widely supported yet).
Some other hints:
In the Box constructor you are making local variables but you really want to assign them to the new Box instance, so use this instead of var:
function Box() {
this.boxx=20;
this.boxy=20;
this.boxWidth=460;
this.boxHeight=360;
}
In the clearBox function, you are using this when it is not set in the call, so it references window. Just get rid of it, you pass box to the function so reference it directly:
function clear(box) {
context.clearRect(box.boxx, box.boxy,
box.boxWidth, box.boxHeight);
}
Same applies to the moveBall and moveAndCheck functions, just get rid of this (I think you should do some research on how this is handled in javascript, there are many articles on it, it's quite different to Java).
Now the balls will bounce around nicely inside the box.
I want to thank the people who contributed to my question and it has been helpful in resolving the issue and the answer I selected was current in a way, but it fixed the problem for a different reason.
Incorrect:
Putting quotes around 'moveBalls(balls, box)' animates things.
What actually fixes the problem is removing the arguments and parentheses from the call to the moveball function. I discovered this when rewriting other parts of my code as the poster suggested.
So for future notice to other people with a similar problem if you need to remove the arguments and use a wrapper function or global variables.

ipad safari stops running code events (canvases with 1 image object) out of memory

(Yeah I'm from sweden so my english might not be perfect ;)
I have troubles with memory on the ipad. This code does not crash the broser, but it just stops. At some point it never goes in to any of the event handlers on the Image object. I have no clue why..? I have searched the forum and googled for a couple of days about workarounds. But they don't really fit what I am trying to achieve(?). (Because I only have 1 Image object).
What I have created is as follows:
1 main canvas which is visible to the user.
16 other canvases which I draw on.
1 Image Object which I load images with.
all images are pngs with alpha and have the following dimension 900x373 px. All the canvases have the same dimensions.
On the 16 other canvases there are drawn 8 images.
The purpose of this is to be able to rotate an object which has interchangable layers.
(1 angle is an object from a different angle, so it will look like it's rotating when the main loop runs.) The rotation is supposed to be controlled by touch/mouse but this should demonstrate what I want to achieve.
I know that this is a lot of code to look through and it might not be the best written code either since I'm a novice at javascript. However it does work fine on my laptop in chrome.
I've read something about events holding references to imageObjects and therefore they would not get GC'd. But does that imply here when I only have one Image Object ?
I also tried adding and removing the listeners with jquery syntax but no success.
It would be much appreciated if anyone would take the time to answer this.
Regards Oscar.
initialization of variables:
var drawingCanvas = null;
var context = null;
var numAngles = 8;
var angleStep = 32/ numAngles;
var canvasAngles = [];
var loadStatus = {};
var basePath = "assets_900/";
var layerPaths = [];
var layerPathsA = ["black/","v16/","f86/","fA00049/","fA00340/","fTG02/","fTG02/","fTJ02/"];
var layerPathsB = ["red/","v16/","f86/","fA00049/","fA00340/","fTG02/","fTG02/","fTJ02/"];
var layerPathsC = ["black/","v16/","f86/","fR134/","fA00340/","fTG02/","fTG02/","fTJ02/"];
var layerPathsD = ["red/","v16/","f86/","fR134/","fA00340/","fTG02/","fTG02/","fTJ02/"];
var layerPathsArr = [layerPathsA,layerPathsB,layerPathsC,layerPathsD];
layerPathsCounter = 0;
var numLayers = layerPaths.length;
var imageInit = null;
var SW = 900; //1920
var SH = 373; //796
var loopcounter = 0;
first setup of canvases and other stuf:
layerPaths = layerPathsArr[0];
drawingCanvas = document.getElementById('myDrawing');
context = drawingCanvas.getContext('2d');
for(var i = 0; i < numAngles; i++){
var canvas = document.createElement('canvas');
var canvasContext = canvas.getContext('2d');
canvasContext.createImageData(SW, SH);
canvas.height = SH;
canvas.width = SW;
canvasAngles.push(canvas);
}
this will init the loop, and then it will never stop, it will jsut continue until mobile safari crashes:
loadImage(0,0,0);
this is a loop that loads images:
when it has loaded 8 images it draws them on one of the 16 canvases and then that canvas gets drawn on the visible canvas. then it loads a new angle and 8 new images , and so on...
function loadImage(pathIndex,layerIndex,angleIndex){
if(layerIndex < layerPaths.length ){
//logger.log("path :" + pathIndex +" lajr : "+ layerIndex + " angl: " + angleIndex);
imageInit = new Image();
imageInit.onload = function(){
var canvas = canvasAngles[angleIndex];
var canvasContext = canvas.getContext('2d');
canvasContext.drawImage(imageInit,0, 0);
imageInit.onload = null;
imageInit.onerror = null;
imageInit.onabort = null;
imageInit.src = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
imageInit = null;
delete imageInit;
loadImage(pathIndex,layerIndex+1,angleIndex);
}
imageInit.onerror = function(){
logger.log("Error loading, retrying....");
loadImage(pathIndex,layerIndex,angleIndex);
}
imageInit.onabort = function(){
logger.log("Error loading (aborted)");
}
var path = "";
if(pathIndex < 10){
path = basePath + layerPaths[layerIndex] + "img000"+ pathIndex + ".png";
}else{
path = basePath + layerPaths[layerIndex] + "img00"+ pathIndex + ".png";
}
imageInit.src = path;
}else{
displayAngle(angleIndex);
if(angleIndex > numAngles-2){
logger.log("turns : " + loopcounter++ +" C: " + layerPathsCounter);
clearCanvases();
loadImage(0,0,0);
layerPathsCounter++;
if(layerPathsCounter > layerPathsArr.length-1){
layerPathsCounter = 0;
}
layerPaths = layerPathsArr[layerPathsCounter];
}else{
loadImage(pathIndex+angleStep,0,angleIndex+1);
}
}
}
heres a couple of helper functions :
function clearCanvases(){
for(var i = 0; i < numAngles; i++){
var canvas = canvasAngles[i];
var canvasContext = canvas.getContext('2d');
canvasContext.clearRect(0,0,drawingCanvas.width, drawingCanvas.height);
}
}
function displayAngle(index){
context.clearRect(0,0,drawingCanvas.width, drawingCanvas.height);
var canvas = canvasAngles[index];
context.drawImage(canvas,0,0);
}
HTML :
<body >
<canvas id="myDrawing" width="900" height="373">
<p>Your browser doesn't support canvas. (HEHE)</p>
</canvas>
</body>
It seems like you can only load ~6.5 mb in one tab. And as far as I know there is no way to free up this memory (?)
this will load about 13 500kb imqages on an ipad and then stop..,
http://www.roblaplaca.com/examples/ipadImageLoading/img.html

Categories

Resources