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 = [];
Related
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'm making a canvas game in JavaScript and have some trouble saving the data. I'm placing images on the canvas with a for-loop and I want to save information for each image in objects. For each image an object.
function CreateBlocks(){
for(var i = 0; i <= blocks; i++){
var img = new Image();
img.src = "/images/Block.png";
blockObject = {
x: x,
y: y,
points: 10
}
ctx.drawImage(img,x,y);
x += 100;
y += 100;
}
}
Now this obviously overwrites the blockObject everytime it loops. I tried adding to loop value to the name of the object like block[i]Object or blockObject[i] but that returns syntax errors.
I could just create a single dimension array for each value, but that seems rather messy to me. How can I create the objects in the loop?
Simply use an array and push the new object each time:
function CreateBlocks(){
var arr = [];
for(var i = 0; i <= blocks; i++){
var img = new Image();
img.src = "/images/Block.png";
arr.push({
x: x,
y: y,
points: 10
});
ctx.drawImage(img,x,y);
x += 100;
y += 100;
}
}
If you create a blockObjects array, your second idea, using the blockObject[i] syntax will work:
var blockObjects=[];
function CreateBlocks(){
for(var i = 0; i <= blocks; i++){
var img = new Image();
img.src = "/images/Block.png";
blockObjects[i] = {
x: x,
y: y,
points: 10
};
ctx.drawImage(img,x,y);
x += 100;
y += 100;
}
}
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 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
I'm trying to update the interval value x but not succeeding. I hope to eventually have different pawn objects with internal values I can update when keypress up/down/left/right to redraw the canvas.
Code Update: Able to update x, y values now, but not sure about creating seperate objects using modular JavaScript pattern.
JavaScript using jQuery 1.5.1:
//Constructors
var pawn = (function() {
var x = 25;
var y = 25;
var getX = function() {
return x;
};
var getY = function() {
return y;
};
function _drawPawn(x,y) {
var x = x || 25;
var y = y || 25;
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var b = $('body');
var winH = b.height();
var winW = b.width();
$(canvas).attr('height',winH).attr('width',winW);
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.lineWidth="3";
ctx.arc(x, y, 10, 0, Math.PI * 2, true); // circle
ctx.stroke();
}
}
function left() {
x = 100;
y = 100;
}
return {
getX:getX,
getY:getY,
draw: function drawPawn(x,y) {
_drawPawn(x,y);
},
left:left
}
})();
//Init
$(function() {
var b = pawn;
b.left();
alert(b.getX());
var a = pawn;
alert(a.getX());
//b.draw();
});
and the html:
<canvas id="canvas" height="800px" width="600px">
Download a modern browser like Internet Explorer 9, Firefox, Safari or Chome to view this.
</canvas>
As it is currently written, your getX() function will be automatically invoked with the supplied (empty) parameter list, and return x, which is then equivalent to:
var getX = x;
which will give getX the value of x at the time it was declared.
To fix, removed the parentheses:
var getX = function() {
return x;
};
You also need to fix this function in your returned object:
draw: function drawPawn(x, y) {
_drawPawn(x, y);
}
Since the apparent intent is to call draw without parameters and use the currently bound x and y values, it should be:
draw: function drawPawn() {
_drawPawn(x, y);
}
First like alnitak said remove paranthese from getX, getY.
If u want different pawn objects you should use var b = new pawn()