I am trying to write a javascript program that renders an 8x8 grid of dirt tiles on an HTML5 canvas. However, when I run this code it throws up error messages when running the draw_terrain() function and it appears to be a problem with the blockArray.length component. Can someone explain to me how to fix this problem? Thanks in advance.
//Define initial canvas variables and images
var canvas;
var ctx;
var WIDTH = 800;
var HEIGHT = 800;
var dirt = new Image();
dirt.src = 'dirt.png';
//Function called to initialise canvas variables and run draw on interval
function init(){
canvas = document.getElementById('myCanvas');
ctx = canvas.getContext('2d');
return setInterval(draw, 15);
}
//Function that generates an 8x8 Array called blockArray
function gen_terrain(){
var blockArray = new Array(8);
for(var i=0; i<blockArray.length; i++) {
blockArray[i] = new Array(8);
for(var j=0; j<blockArray[i].length; j++){
blockArray[i][j] = 0;
};
};
}
//Function that returns a random number between a min and max
function randomRange (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function draw_terrain(){
for(var i=0; i<blockArray.length; i++) {
for(var j=0; j<blockArray[i].length; j++){
ctx.drawImage(dirt,(n-1)*32,(j-1)*32);
};
};
}
function draw(){
draw_terrain();
}
gen_terrain();
init();
Your problem, as other people have explained is that the variable you are using the build the array will not exist by the time the draw occurs. Just place your array declaration outside of the function and your issue will go away.
See comment below:
function init(){
canvas = document.getElementById('myCanvas');
ctx = canvas.getContext('2d');
return setInterval(draw, 15);
}
//Function that generates an 8x8 Array called blockArray
var blockArray = []; // <== has to be global (outside function).
function gen_terrain(){
// Not here -> var blockArray = [];
for(var i=8; i--;) {
blockArray[i] = [0,0,0,0,0,0,0,0];
};
}
Example
Related
var NUM_CIRCLES = 15;
var MIN_RADIUS = 10;
var MAX_RADIUS = 40;
var DELAY = 500;
function start(){
drawCircles();
setTimer(update, DELAY);
}
function drawCircles(){
for(var i = 0; i < NUM_CIRCLES; i++){
circle = new Circle(Randomizer.nextInt(MIN_RADIUS, MAX_RADIUS));
var x = Randomizer.nextInt(MIN_RADIUS, getWidth() - MAX_RADIUS);
var y = Randomizer.nextInt(MIN_RADIUS, getHeight() - MAX_RADIUS);
circle.setPosition(x, y);
add(circle);
}
}
function update(){
var colors = Randomizer.nextColor();
circle.setColor(colors);
}
Write a program that creates a list of NUM_CIRCLES circles on the screen of different sizes from MIN_RADIUS to MAX_RADIUS. Every DELAY milliseconds, get a random color and change all of the circles to this color. All the circles should have the same color at all times.
First, you should declare circle as a global variable since you use circle in multiple functions. Next you should get rid of the drawCircles function in the start function and put it inside the timer. Then call the update function in your drawCircles function.
This is the revised code.
var NUM_CIRCLES = 15;
var MIN_RADIUS = 10;
var MAX_RADIUS = 40;
var DELAY = 500;
var circle;
function start(){
setTimer(drawCircles, DELAY);
}
function drawCircles(){
for(var i = 0; i < NUM_CIRCLES; i++){
circle = new Circle(Randomizer.nextInt(MIN_RADIUS, MAX_RADIUS));
var x = Randomizer.nextInt(MIN_RADIUS, getWidth() - MAX_RADIUS);
var y = Randomizer.nextInt(MIN_RADIUS, getHeight() - MAX_RADIUS);
update();
circle.setPosition(x, y);
add(circle);
}
}
function update(){
var colors = Randomizer.nextColor();
circle.setColor(colors);
}
setTimer(update, DELAY);
here when you call your update function you do not have () after update change it to:
setTimer(update(), DELAY);
I am a beginer. I want to create a pixel art site. For this I try to develope my javascript code. Now I am on the way to simplify the code by setting the different rectangles using var as object and array to avoid to type milles of lines. Than I think to create at the second part an array constructor with defining coords(other x, other y) for every single rectangle in 2D array.
At the moment I don't relise why the first part of the code is not working. Can you please suggest your mind? Thanks a lot in advance.
Here is my code (link on JS Bin):
var canvas;
var ctx;
var x = 0;
var y = 0;
var w = 10; // Width=10px
var h = w; // Heigth=10px
function init() {
canvas = document.querySelector('#myCanvas');
ctx = canvas.getContext('2d');
draw();
}
// Create a rect by path method for restoring the buffer
var rect;
function draw(){
ctx.beginPath();
ctx.rect(x,y,w,h);
}
var c = ['#66757F', '#F7F7F7', '#CCD6DD']; // Setting a color palette as an array
for (var i=0; i<c.length; i++){
c[i]=ctx.fillStyle();
}
// Define colored rectangles as the Objects
var r1 = {rect;[0]}
var r2 = {rect;[1]}
var r3 = {rect;[2]}
ctx.fill();
// Setting three rectangle by diagonal
var r=[r1,r2,r3];// Array of setted rectangles
function draw(){
for (var j=0; j<r.length; j++){
r[j]=ctx.moveTo(x+w*j,y+h*j);
}
}
for (var j=0; j<r.length; i++){
r[j]=ctx.moveTo(x+w*j,y+h*j);
}
You typed 'i++' when using the letter 'j'.
Not sure whether this solves the problem.
Why do you use Math.abs in
var w = Math.abs(-10); // Width=10px
Isn't it easier to set 'var w' to 10 ?
var w = 10;
Is what you're looking for how to create classes and make objects from that class?
If so this is how you would create a class and make objects.
//This will hold all of your objects.
var listOfObjects = [];
//This is a class. You can create objects with it.
function myClass() {
//location
this.X = 0;
this.Y = 0;
//size
this.width = 5;
this.height = 5;
}
function CreateNewObject() {
//This will create and add an object of myClass to your array.
//Now you can loop through the array and modify the values as you wish.
listOfObjects.push(new myClass());
}
I've spent about 12 hours looking through this code, and fiddling with it, trying to find out where there's a recursion problem because I'm getting the, "maximum call stack size exceeded," error, and haven't found it. Someone smarter than me please help me!
so far, all I found was that when I make the object, spot, a circle, object, the problem disappears, but when I make it a, 'pip', I get this stack overflow error. I've gone over the pip class with a friggin' microscope, and still have no idea why this is happening!
var canvas = document.getElementById('myCanvas');
//-------------------------------------------------------------------------------------
// Classes
//-------------------------------------------------------------------------------------
//=====================================================================================
//CLASS - point
function point(x,y){
this.x = x;
this.y = y;
}
//=====================================================================================
// CLASS - drawableItem
function drawableItem() {
var size = 0;
this.center = new point(0,0);
this.lineWidth = 1;
this.dependentDrawableItems = new Array();
}
//returns the size
drawableItem.prototype.getSize = function getSize(){
return this.size;
}
// changes the size of this item and the relative size of all dependents
drawableItem.prototype.changeSize = function(newSize){
var relativeItemSizes = new Array;
relativeItemSizes.length = this.dependentDrawableItems.length;
// get the relative size of all dependent items
for (var i = 0; i < this.dependentDrawableItems.length; i++){
relativeItemSizes[i] = this.dependentDrawableItems[i].getSize() / this.size;
}
// change the size
this.size = newSize;
// apply the ratio of change back to all dependent items
for (var i = 0; i < relativeItemSizes.length; i++){
this.dependentDrawableItems[i].changeSize(relativeItemSizes[i] * newSize);
}
}
//moves all the vertices and every dependent to an absolute point based on center
drawableItem.prototype.moveTo = function(moveX,moveY){
//record relative coordinates
var relativeItems = new Array;
relativeItems.length = this.dependentDrawableItems.length;
for (var i = 0; i < relativeItems.length; i++){
relativeItems[i] = new point;
relativeItems[i].x = this.dependentDrawableItems[i].center.x - this.center.x;
relativeItems[i].y = this.dependentDrawableItems[i].center.y - this.center.y;
}
//move the center
this.center.x = moveX;
this.center.y = moveY;
//move all the items relative to the center
for (var i = 0; i < relativeItems.length; i++){
this.dependentDrawableItems[i].moveItemTo(this.center.x + relativeItems[i].x,
this.center.y + relativeItems[i].y);
}
}
// draws every object in dependentDrawableItems
drawableItem.prototype.draw = function(ctx){
for (var i = 0; i < this.dependentDrawableItems.length; i++) {
this.dependentDrawableItems[i].draw(ctx);
}
}
//=====================================================================================
//CLASS - circle
function circle(isFilledCircle){
drawableItem.call(this);
this.isFilled = isFilledCircle
}
circle.prototype = new drawableItem();
circle.prototype.parent = drawableItem.prototype;
circle.prototype.constructor = circle;
circle.prototype.draw = function(ctx){
ctx.moveTo(this.center.x,this.center.y);
ctx.beginPath();
ctx.arc(this.center.x, this.center.y, this.size, 0, 2*Math.PI);
ctx.closePath();
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.outlineColor;
if (this.isFilled === true){
ctx.fill();
}else {
ctx.stroke();
}
this.parent.draw.call(this,ctx);
}
//=====================================================================================
//CLASS - pip
function pip(size){
circle.call(this,true);
}
pip.prototype = new circle(false);
pip.prototype.parent = circle.prototype;
pip.prototype.constructor = pip;
//----------------------------------------------------------------------
// Objects/variables - top layer is last (except drawable area is first)
//----------------------------------------------------------------------
var drawableArea = new drawableItem();
var spot = new pip();
spot.changeSize(20);
drawableArea.dependentDrawableItems[drawableArea.dependentDrawableItems.length] = spot;
//------------------------------------------
// Draw loop
//------------------------------------------
function drawScreen() {
var context = canvas.getContext('2d');
context.canvas.width = window.innerWidth;
context.canvas.height = window.innerHeight;
spot.moveTo(context.canvas.width/2, context.canvas.height/2);
drawableArea.draw(context);
}
window.addEventListener('resize', drawScreen);
Here's the demo: http://jsfiddle.net/DSU8w/
this.parent.draw.call(this,ctx);
is your problem. On a pip object, the parent will be circle.prototype. So when you now call spot.draw(), it will call spot.parent.draw.call(spot), where this.parent is still the circle.prototype…
You will need to explicitly invoke drawableItem.prototype.draw.call(this) from circle.prototype.draw. Btw, you should not use new for the prototype chain.
Why would you write code like that? It's so difficult to understand and debug. When I'm creating lots of classes I usually use augment to structure my code. This is how I would rewrite your code:
var Point = Object.augment(function () {
this.constructor = function (x, y) {
this.x = x;
this.y = y;
};
});
Using augment you can create classes cleanly. For example your drawableItem class could be restructured as follows:
var DrawableItem = Object.augment(function () {
this.constructor = function () {
this.size = 0;
this.lineWidth = 1;
this.dependencies = [];
this.center = new Point(0, 0);
};
this.changeSize = function (toSize) {
var fromSize = this.size;
var ratio = toSize / fromSize;
this.size = toSize;
var dependencies = this.dependencies;
var length = dependencies.length;
var index = 0;
while (index < length) {
var dependency = dependencies[index++];
dependency.changeSize(dependency.size * ratio);
}
};
this.moveTo = function (x, y) {
var center = this.center;
var dx = x - center.x;
var dy = y - center.y;
center.x = x;
center.y = y;
var dependencies = this.dependencies;
var length = dependencies.length;
var index = 0;
while (index < length) {
var dependency = dependencies[index++];
var center = dependency.center;
dependency.moveTo(center.x + dx, center.y + dy);
}
};
this.draw = function (context) {
var dependencies = this.dependencies;
var length = dependencies.length;
var index = 0;
while (index < length) dependencies[index++].draw(context);
};
});
Inheritance is also very simple. For example you can restructure your circle and pip classes as follows:
var Circle = DrawableItem.augment(function (base) {
this.constructor = function (filled) {
base.constructor.call(this);
this.filled = filled;
};
this.draw = function (context) {
var center = this.center;
var x = center.x;
var y = center.y;
context.moveTo(x, y);
context.beginPath();
context.arc(x, y, this.size, 0, 2 * Math.PI);
context.closePath();
context.lineWidth = this.lineWidth;
context[this.filled ? "fill" : "stroke"]();
base.draw.call(this, context);
};
});
var Pip = Circle.augment(function (base) {
this.constructor = function () {
base.constructor.call(this, true);
};
});
Now that you've created all your classes you can finally get down to the drawing:
window.addEventListener("DOMContentLoaded", function () {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var drawableArea = new DrawableItem;
var spot = new Pip;
spot.changeSize(20);
drawableArea.dependencies.push(spot);
window.addEventListener("resize", drawScreen, false);
drawScreen();
function drawScreen() {
var width = canvas.width = window.innerWidth;
var height = canvas.height = window.innerHeight;
spot.moveTo(width / 2, height / 2);
drawableArea.draw(context);
}
}, false);
We're done. See the demo for yourself: http://jsfiddle.net/b5vNk/
Not only have we made your code more readable, understandable and maintainable but we have also solved your recursion problem.
As Bergi mentioned the problem was with the statement this.parent.draw.call(this,ctx) in the circle.prototype.draw function. Since spot.parent is circle.prototype the this.parent.draw.call(this,ctx) statement is equivalent to circle.prototype.draw.call(this,ctx). As you can see the circle.prototype.draw function now calls itself recursively until it exceeds the maximum recursion depth and throws an error.
The augment library solves this problem elegantly. Instead of having to create a parent property on every prototype when you augment a class augment provides you the prototype of that class as a argument (we call it base):
var DerivedClass = BaseClass.augment(function (base) {
console.log(base === BaseClass.prototype); // true
});
The base argument should be treated as a constant. Because it's a constant base.draw.call(this, context) in the Circle class above will always be equivalent to DrawableItem.prototype.draw.call(this, context). Hence you will never have unwanted recursion. Unlike this.parent the base argument will alway point to the correct prototype.
Bergi's answer is correct, if you don't want to hard code the parent name multiple times you could use a helper function to set up inheritance:
function inherits(Child,Parent){
Child.prototype=Object.create(Parent.prototype);
Child.parent=Parent.prototype;
Child.prototype.constructor=Child;
};
function DrawableItem() {
this.name="DrawableItem";
}
DrawableItem.prototype.changeSize = function(newSize){
console.log("changeSize from DrawableItem");
console.log("invoking object is:",this.name);
}
function Circle(isFilledCircle){
Circle.parent.constructor.call(this);
this.name="Circle";//override name
}
inherits(Circle,DrawableItem);
Circle.prototype.changeSize = function(newSize){
Circle.parent.changeSize.call(this);
console.log("and some more from circle");
};
function Pip(size){
Pip.parent.constructor.call(this,true);
this.name="Pip";
}
inherits(Pip,Circle);
var spot = new Pip();
spot.changeSize();
For a polyfill on Object.create look here.
I am using the following code to try out the canvas element
Shape = function(x,y){
this.x = x;
this.y = y;
};
shapes = new array();
shapes.push(new Shape(50,50,10,10));
shapes.push(new Shape(100,100,10,10));
shapes.push(new Shape(150,150,10,10));
function animate(){
context.clearRect(0,0, canvas.width(),canvas.height());
var shapesLength = shapes.length;
for(var i = 0; i < shapesLength; i++){
var tmpShape = shapes[i];
tmpShape.x++;
context.fillRect(tmpShape.x,thmpShape.y,10,10);
}
context.fillStyle = 'yellow';
context.fill();
if(playAnimation){
setTimeout(animate, 1)
}
}
However, when I run this in the browser I get the following error -
ReferenceError: array is not defined
shapes = new array();
I have tried making it a global and local variable I just cant see where I am going wrong?
array should be capitalized, as that is the name of the constructor:
shapes = new Array();
In addition it's better to use the square bracket notation to create an array. Like this:
shapes = [];
I have a project regarding the Dijkstra's algorithm with a canvas to draw the graph. I have the following code in the JavaScript of my html file. This is a portion of JavaScript code.
var graph = {};
var i = 0;
function ondblclick(e)
{
var cell = getpos(e)
canvasX = cell.x-10;
canvasY = cell.y-10;
graph = {i: {coord : [cell.x,cell.y]}};
var g_canvas = document.getElementById("canvas");
var context = g_canvas.getContext("2d");
var cat = new Image();
cat.src = "dot1.png";
cat.onload = function()
{
context.drawImage(cat,canvasX , canvasY);
}
}
i = i+1
function submit()
{
for (i = 0; i<= 5; i = i + 1)
alert(graph.i.coord);
}
here I have a canvas and the 'ondblclick' is the function when the user double clicks on the canvas. Canvas is used to draw nodes and edges. 'ondblclick' is used for drawing the nodes.
My issue is on saving the coordinate values of the nodes to the 'graph' in dictionary format. it accepts only the last value. How can I update/append/extend the all values to the same dictionary using the variables I and cell.x,cell.y values??
You nearly had it.
var graph = [];
function ondblclick(e)
{
var cell = getpos(e)
canvasX = cell.x-10;
canvasY = cell.y-10;
graph.push({coord : [cell.x,cell.y]});
var g_canvas = document.getElementById("canvas");
var context = g_canvas.getContext("2d");
var cat = new Image();
cat.src = "dot1.png";
cat.onload = function()
{
context.drawImage(cat,canvasX , canvasY);
}
}
function submit()
{
for (i = 0; i<= 5; i = i + 1)
alert(graph[i].coord);
}
In order to get data into graph you need to push objects into an array, then loop through that array. Your problem was that you used the i variable incorrectly, thus storing (and retrieving) your coords ontop of each other.
A dictionary (key-value store) is the wrong data structure for a graph. Use an array where you can append nodes. Use a dictionary for each element in the array to save coordinates in them.