Initiation of an object inside another - javascript

I have a piece of js software that is structured like so:
obj = new object[id]();
function wrapperFunction (e) {
var pos = findPos(this);
e._x = e.pageX - pos.x;
e._y = e.pageY - pos.y;
var func = obj[e.type];
if (func) {
func(e);
}
}
__
obj.line = function () {
this.started = false;
this.mousedown = function (e) {
}
this.mousemove = function (e) {
if (this.started) {
}
}
this.mouseup = function (e) {
if (this.started) {
}
}
}
The above code block is duplicated for multiple shapes so there is also a obj.square obj.circle etc...
I also have a shape object that is as follows.
function Shape (type, color, height, width, radius, x, y) {
this.type = type;
this.color = color;
this.h = height;
this.w = width;
this.r = radius;
this.points = ["x","y"];
this.points["x"] = [x];
this.points["y"] = [y];
};
I would like to initiate the shape object on a mousedown for each obj.* and populate the shape object with the propper info.
Now for the issue.
The radius is calcuated on every mousemove as well as height and width but when I add shapes = new Shape(circle, black, 10, 10, null, e._x, e._y) to the mousemove so it looks like...
this.mousemove = function (e) {
if (this.started) {
shapes = new Shape(circle, black, 10, 10, null, e._x, e._y);
}
}
The shape object does not create.
If I create the shape object inside the wrapper function instead of the mousemove then the object initiates but I cannot use radius or height/width.
How can I create an object inside another object inside a wrapper function so I can use calculated terms inside the created object? Is there an alternate route to take besides what I am doing?

Aside from wonkiness in the obj = new object[this.id](); line, I think you're just missing a this keyword:
this.mousemove = function (e) {
if (this.started) {
this.shapes = new Shape(circle, black, 10, 10, null, e._x, e._y);
}
}
Edit just noticed more wonkiness in your code (yes, that's a technical term :). I think you want to change these lines in the constructor:
this.points = ["x","y"]; // creates an array, which is indexed by numbers
this.points["x"] = [x]; // tacks on some ad-hoc properties to the array, which
this.points["y"] = [y]; // doesn't really make sense
to this:
this.points = {x: x, // I think this is what you actually mean to do.
y: y};

Related

JavaScript prototype inheritance and html canvas

I'm a Ruby developer who finally decided to learn JavaScript seriously. So I purchased some books and I started to dive in, but I got stuck quickly when I tried to understand prototypal inheritance...
One of the examples of the book is the following.
Given a Shape which prototype has a draw method, and two child shapes: a Triangle and a Rectangle which prototype inherit from Shape;
when I call the draw function on Triangle and Rectangle instances the method will draw them properly.
when I add a second method to show their name, every instance will log it properly.
Everything was understandable perfectly until I added a third method to fill the shapes... And only the last one get filled. no matter which one I call. Why? Is there something special in canvas?
Here is the code of the exercise:
function Point(x, y) {
this.x = x;
this.y = y;
}
function Shape() {
this.points = [];
this.init();
}
Shape.prototype = {
constructor: Shape,
init: function() {
if (this.context === undefined) {
Shape.prototype.context = document.getElementById('canvas').getContext('2d');
};
if (this.name === undefined) {
Shape.prototype.name = 'generic shape'
}
},
draw: function() {
var i, ctx = this.context;
ctx.strokeStyle = 'rgb(0,0,255)';
ctx.beginPath();
ctx.moveTo(this.points[0].x, this.points[0].y);
for (i = 1; i < this.points.length; i++) {
ctx.lineTo(this.points[i].x, this.points[i].y);
}
ctx.closePath();
ctx.stroke();
},
fill: function(color) {
var ctx = this.context;
ctx.fillStyle = color;
ctx.fill();
},
say_name: function() {
console.log('Hello my name is ' + this.name)
}
};
function Triangle(a, b, c) {
this.points = [a, b, c];
this.name = 'Triangle'
this.context = document.getElementById('canvas').getContext('2d');
}
function Rectangle(side_a, side_b) {
var p = new Point(200, 200);
this.points = [
p,
new Point(p.x + side_a, p.y), // top right
new Point(p.x + side_a, p.y + side_b), // bottom right
new Point(p.x, p.y + side_b) // bottom left
];
this.name = 'Rectangle'
this.context = document.getElementById('canvas').getContext('2d');
}
(function() {
var s = new Shape();
Triangle.prototype = s;
Rectangle.prototype = s;
})();
function testTriangle() {
var p1 = new Point(100, 100);
var p2 = new Point(300, 100);
var p3 = new Point(200, 0);
return new Triangle(p1, p2, p3);
}
function testRectangle() {
return new Rectangle(100, 100);
}
function make_me_crazy() {
var t = testTriangle();
var r = testRectangle();
t.draw();
r.draw();
t.say_name();
r.say_name();
t.fill('red');
}
make_me_crazy();
<canvas height='600' width='800' id='canvas' />
Thank you!
More details:
Why the function say_name is working exactly I expect saying: 'I am a triangle' or 'I am a rectangle' and never 'I am a generic shape', but the fill function fills the rectangle despite I'm calling it on a triangle instance? As people rightly answered to flip the two draw functions calls, I would specify better the following. The problem is not about the color of a shape, but the context pointer. why only the last shape is filled? If I add more shapes before calling fill only the last one get filled. This means I'm doing something wrong referring to the canvas. I supposed it was "the place where I draw shapes" but it seems more like "the last active shape"
How can I fix that code to make it working correctly filling the shape I want whenever I want? I mean. what if I want to have a function which receive an instance of a particular shape and fills it?
Is there any way to access a the draws contained into a canvas?
The core of the problem is the context - your shapes are sharing the single context of the canvas, and therefore it is not straight-forward to flip back and forth between objects. Instead, think of your order-of-operations as handling a single shape at a time and only moving on to the next one when you are done with the former.
Note the order of calls in the make_me_crazy function:
function Point(x, y) {
this.x = x;
this.y = y;
}
function Shape() {
this.points = [];
this.init();
}
Shape.prototype = {
constructor: Shape,
init: function(){
if (this.context === undefined) {
Shape.prototype.context = document.getElementById('canvas').getContext('2d');
};
if(this.name === undefined){
Shape.prototype.name = 'generic shape'
}
},
draw: function(){
var i, ctx = this.context;
ctx.strokeStyle = 'rgb(0,0,255)';
ctx.beginPath();
ctx.moveTo(this.points[0].x, this.points[0].y);
for (i = 1; i<this.points.length; i++) {
ctx.lineTo(this.points[i].x, this.points[i].y);
}
ctx.closePath();
ctx.stroke();
},
fill: function(color){
var ctx = this.context;
ctx.fillStyle = color;
ctx.fill();
},
say_name: function(){console.log('Hello my name is '+ this.name)}
};
function Triangle(a,b,c){
this.points = [a, b, c];
this.name = 'Triangle'
this.context = document.getElementById('canvas').getContext('2d');
}
function Rectangle(side_a, side_b){
var p = new Point(200, 200);
this.points = [
p,
new Point(p.x + side_a, p.y),// top right
new Point(p.x + side_a, p.y + side_b), // bottom right
new Point(p.x, p.y + side_b)// bottom left
];
this.name = 'Rectangle'
this.context = document.getElementById('canvas').getContext('2d');
}
(function(){
var s = new Shape();
Triangle.prototype = s;
Rectangle.prototype = s;
})();
function testTriangle(){
var p1 = new Point(100, 100);
var p2 = new Point(300, 100);
var p3 = new Point(200, 0);
return new Triangle(p1, p2, p3);
}
function testRectangle(){
return new Rectangle(100, 100);
}
function make_me_crazy(){
var t = testTriangle();
t.say_name();
t.draw();
t.fill('red');
var r = testRectangle();
r.draw();
r.say_name();
}
make_me_crazy();
<canvas height='600' width='800' id='canvas'></canvas>
About the points of your question.
For the first one: the key is this line of code
if(this.name === undefined){
Shape.prototype.name = 'generic shape'
}
When you instantiate Rectangle and Triangle, both of them set name.
In the other hand, the render method is only available in the Shape prototype.
About the second point (and the third one):
Maybe are you painting the Rectangle over the Triangle. Try to switch the order of the draw calls to check it.

p5js instance mode and object orientation

I have a "sketch.js" where i want to instance multiple canvases und display different objects of one class in them.
sketch.js:
var myObjects = [];
var sketch1 = function (p) {
p.setup = function () {
p.createCanvas(600, 400);
}
p.draw = function () {
p.background(51);
p.rect(p.width/2, p.height/2, 200, 200);
}
};
new p5(sketch1, "canvas_container");
var sketch2 = function (p) {
p.setup = function () {
p.createCanvas(600, 400);
myObjects.push(new myObject(p, 1, 2));
myObjects.push(new myObject(p, 3, 4));
}
p.draw = function () {
p.background();
for (var i=0; i<myObjects.length; i++) {
p.line(0, 0, myObjects[i].x, myObjects[i].y);
myObjects[i].doSomething(Math.random()*10);
}
}
};
new p5(sketch2, "canvas_container");
When do i use "this." and when "p." in this case?
Furthermore I would like to use some "other" methods from the p5 library in my sketch.js file, outside of the instaces, like:
select('..') ...
but I get the error:
select is not defined
I found myself a dirt workaround:
new p5().select('...') ...
What is the clean way to do this?
myObjects.js
function myObject(canvas, x, y) {
this.canvas = canvas;
this.x = x;
this.y = y;
this.doSomething = function(rad) {
this.canvas.ellipse(this.x, this.y, rad, rad);
}
}
Has anybody an example for handeling multiple instances of canvases?
Note that right now, you aren't ever creating a new instance of myObject. Your array is named myObjects, but you're only ever adding p (which is an instance of p5) to it. So you can't magically call your doSomething() function on the objects you've added to your myObjects array, because they aren't myObject objects. They're p5 objects.
I think what you would want to do is take the p variable passed into each of your sketches and pass that into your "class" functions. Something like this:
p.setup = function () {
p.createCanvas(600, 400);
var myObjectOne = new myObject(p, 1, 2);
var myObjectTwo = new myObject(p, 3, 4);
}
Then it should work how you're expecting it to.

Updating properties of JS "class" based on other properties?

I'm relatively new to Javascript and I am trying to create a very simple physics engine for a game type project I am working on. In order to do this, I created what I understand to be the JS equivalent of a class that I can create new copies of for each object I want. The problem is that I want to be able to update a value such as the x position and have this also update things such as the x Middle position (x center of object on screen). I know this is possible by using an object literal and the getter, however I want to be able to create new objects at realtime based on what's on the screen and I couldn't figure out how to use get to make this work. Here's the general idea of what I am trying to do:
var object = function (xPos, yPos, width, height) {
this.xPos = xPos;
this.yPos = yPos;
function getXMid (xP) { return xP + width/2; }
this.xMid = getXMid (this.xPos);
function getYMid (yP) { return yP + height/2; }
this.yMid = getYMid (this.yPos);
}
var ball = new object (10, 20, 50, 50);
ball.xPos = 50;
console.log (ball.xMid); // want this to output 75 instead of 45
You're changing one property, and expecting other properties to update, unfortunately it doesn't work that way when the properties hold primitive values.
You could use setters and getters and a function to update the other properties when you set a value
var object = function(xPos, yPos, width, height) {
this._xPos = xPos;
this._yPos = yPos;
this.recalc = function() {
this.xMid = getXMid(this.xPos);
this.yMid = getYMid(this.yPos);
}
Object.defineProperty(this, 'xPos', {
get: function() {
return this._xPos;
},
set: function(v) {
this._xPos = v;
this.recalc();
}
});
Object.defineProperty(this, 'yPos', {
get: function() {
return this._yPos;
},
set: function(v) {
this._yPos = v;
this.recalc();
}
});
function getXMid(xP) { return xP + width / 2; }
function getYMid(yP) { return yP + height / 2; }
this.recalc();
}
var ball = new object(10, 20, 50, 50);
ball.xPos = 50;
console.log (ball.xMid); // want this to output 75 instead of 45

Douglas Crockfords - how to call base method into an inherited class

I'm trying to construct a base class Shape using Crockford's inheritance pattern. Using this base Shape, I'm trying to draw a circle, a rectangle and a triangle. I'm kinda stuck. I didn't know how to call/modify the base method
function points(x,y) {
x = this.x;
y = this.y;
}
function Shape() {
return {
this.points: [ ],
init : function(){
if(typeof this.context === ‘undefined’){
var canvas = document.getElementById(‘canvas’);
var context = canvas.getContext(‘2d’);
}
},
draw: function(){
var context = this.context;
context.beginPath();
context.moveTo(this.points[0].x, this.points[0].y);
for(var i=1; i< this.parameter.length; i++){
context.lineTo(this.parameter[i].x, this.parameter[i].y);
}
context.closePath();
context.stroke();
}
};
}
function Circle(x, y, r){
var points = Shape();
point.x = x;
points.y = y;
points.r = r;
var baseMethod = that.draw;
that.draw = function(){
/*how to modify the base method to draw circle*/
};
}
function Rectangle(a, b, c, d){
var points = Shape();
point.a = a;
points.b = b;
points.c = c;
points.d = d
var baseMethod = that.draw;
that.draw = function(){
/*how to call base method to draw rectangle*/
};
}
You've got quite a few problems going on with your code. Firstly you need to make sure you've got your basic drawing code working before moving on to more complicated shapes such as circles and rectangles. Start with drawing lines. I've tidied up your code and got it working with drawing straight lines:
//returns basic point object which has
//two properties x & y
function point(x, y) {
return {
x: x,
y: y
}
}
//function that returns a shape object with all the
//mechanisms for drawing lines between points
function Shape(canvasID) {
return {
points: [], //not 'this.points' (which would most likely be window.points)
addPoint: function(x, y) {//adding a point to a shape is an operation of shape
this.points.push(point(x, y))
},
init: function() {
if (typeof this.context === 'undefined') {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
this.context = ctx; //add the context reference to the current shape object
}
},
draw: function() {
this.init();
var context = this.context;
context.beginPath();
var that = this; //create a local reference to the current 'this' object.
//insures us against any possible 'this' scope problems
context.moveTo(that.points[0].x, that.points[0].y);
for (var i = 1; i < that.points.length; i++) {
context.lineTo(that.points[i].x, this.points[i].y);
}
context.closePath();
context.stroke();
}
};
}
//Simple Line object - good for testing your
//basic drawing functionality
function Line(canvasID, x, y, x2, y2) {
var shape = Shape(canvasID);
shape.addPoint(x, y);
shape.addPoint(x2, y2);
shape.draw();
}
//Execute your drawing functionality after the
//window has loaded to make sure all your objects exist before
//trying to use them
window.onload = function() {
Line('canvas', 100, 100, 200, 200);
}
I'm not necessarily sold on whether this is the best way to approach what you are doing - but DC's basic approach is to create objects without having to use the "new" keyword. So he returns an object from a function call using the JavaScript object notation.
Now that you can draw a line, the next step is to draw a series of connected lines one after the other (a path). After that, create your rectangle. You need some code to tell your code where to start drawing the rectangle (the start x/y coordinate) and then you can have parameters denoting the height and width of the rectangle which will be used to calculate the coordinates of the rectangle's corners and passed to the shape object to be drawn in the same way the series of connected lines were drawn. One caveat, though, is to check if there is some sort of 'createRectangle' function on the context object (and same for circle). I don't actually know myself as I've not done this sort of work in HTML5/canvas - although I have in other environments.
Edit
Forgot to mention that you will need to make sure the doctype declaration of your html is html5. A lot of IDE's will automatically declare your html as html4. Html5 just needs: <!DOCTYPE html>
Also, make sure you declare a canvas element in the html body, something like this:
<canvas id="canvas" width="300" height="150">
</canvas>

How to create separate objects using Modular JavaScript pattern

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()

Categories