I've the below JS code using JS classes, that multiple rectangles got generated, in different random locations,
the static rectangle start moving upon click, and the moving rectangle is destroyed if clicked.
I want to re-write the same code in Dart language, I did not know what shall I use instead of the JS myRect.prototype in dart, considering that I prefer not to include all the class properties and functions inside the main class { } any idea?
var NS="http://www.w3.org/2000/svg";
var SVG=function(h,w){
var svg=document.createElementNS(NS,"svg");
svg.width=w;
svg.height=h;
return svg;
}
var svg=SVG(1200,1500);
document.body.appendChild(svg);
class myRect {
constructor(x,y,h,w,fill,name) {
this.name=name;
this.SVGObj= document.createElementNS(NS,"rect");
self = this.SVGObj;
self.x.baseVal.value=x;
self.y.baseVal.value=y;
self.width.baseVal.value=w;
self.height.baseVal.value=h;
self.style.fill=fill;
self.addEventListener("click",this,false);
}
}
Object.defineProperty(myRect.prototype, "draw", {
get: function() {
return this.SVGObj;
}
});
myRect.prototype.handleEvent= function(evt){
self = this.SVGObj;
switch (evt.type){
case "click":
if (typeof self.moving == 'undefined' || self.moving == false) self.moving = true;
else self.moving = false;
if(self.moving == true)
self.move = setInterval(()=>this.animate(),100);
else{
clearInterval(self.move);
self.parentNode.removeChild(self);
}
break;
default:
break;
}
}
myRect.prototype.step = function(x,y) {
return svg.createSVGTransformFromMatrix(svg.createSVGMatrix().translate(x,y));
}
myRect.prototype.animate = function() {
self = this.SVGObj;
self.transform.baseVal.appendItem(this.step(1,1));
};
for (var i = 0; i < 100; i++) {
var x = Math.random() * 500,
y = Math.random() * 300;
var r= new myRect(x,y,10,10,'#'+Math.round(0xffffff * Math.random()).toString(16),'this is my name');
svg.appendChild(r.draw);
}
You can't add methods to a class or object dynamically in Dart. Your options are:
Put them inside the class
Make them functions that take the rectangle as an argument
I would go with (2), just make them functions that accept an instance of your class:
handleEvent(myRect, evt){
// ...
}
Related
I have a class dynObj, but it appears that seperate instances of it adopt the values of the most recently defined instance.
draw() {
tmov1 = new dynObj(args...); //Displays as a white ball on webpage, as intended
tmov2 = new dynObj(different args...); //Seemingly overwrites the properties of tmov1
objects.push(tmov1, tmov2)
for (var i in objects) {
objects[i].dostuff() //Normally causes the object to display as intended,
}; //but will only ever display one
};
The class dynObj is as follows:
class baseObj {
constructor(position, dimentions, properties) {
this.pos = createVector(position.x,position.y) || null;
this.shape = properties.shape
if (this.shape == "ellipse") {
this.dim = dimentions || {diam:0}
} else if (this.shape == "quadrilateral") {
this.dim = dimentions || { x: 0, y: 0 };
}
};
};
class dynObj extends baseObj {
constructor(position, dimentions, suvat, properties) {
super(position, dimentions, properties);
self = this
self.type = 'dynamic'
self.properties = properties
//more definitions with self.x = someval
};
getDistance(a,b){
if (a == undefined || b == undefined) return false;
var dist = p5.Vector.sub(b,a)
//console.log(dist)
return dist
};
tick(ticksize) {
self.assignLastTick(function(lasttick){
self.lasttick = lasttick
self.time = self.time + ticksize
self.updateSuvat(ticksize)
})
};
//assorted methods...
}
Why do the instances affect eachother?
(Can supply a link to this in action if more context is needed)
The problem is that you're creating a global variable self, and using that instead of this. All the instances are accessing the same global variable, which contains the value of this from the last object that was created.
In the callback function in tick(), you need a way to reference the original object, so you need to bind a local variable self there, rather than using a global variable. See How to access the correct `this` inside a callback?
class dynObj extends baseObj {
constructor(position, dimentions, suvat, properties) {
super(position, dimentions, properties);
this.type = 'dynamic'
this.properties = properties
//more definitions with this.x = someval
};
getDistance(a,b){
if (a == undefined || b == undefined) return false;
var dist = p5.Vector.sub(b,a)
//console.log(dist)
return dist
};
tick(ticksize) {
let self = this;
this.assignLastTick(function(lasttick){
self.lasttick = lasttick
self.time = self.time + ticksize
self.updateSuvat(ticksize)
})
};
//assorted methods...
}
I am using the module pattern for my JavaScript "classes". Is there any significant downside to declaring a var self outisde of the class I am returning and then setting it to this inside the class constructor so that I don't have to worry about the context switching when I don't want it to. In this small example it's probably unnecessary, this is just an example.
Example:
var Seat = (function() {
var self = null;
function Seat(startX, startY, inputSeatNumber, inputTableNumber) {
self = this;
self.radius = 10;
self.x = startX; self.y = startY;
self.seatNumber = inputSeatNumber;
self.tableNumber = inputTableNumber;
}
Seat.prototype.moveTo = function(newX, newY) {
if(newX >= 0 && newY >= 0) {
self.x = newX; self.y = newY;
}
};
return Seat;
})();
EDIT: example added
var SeatingChartView = (function() {
function SeatingChartView(canvas_id, seatingChartController, seatForm) {
this.stage = new createjs.Stage(canvas_id);
this.controller = seatingChartController;
this.seatForm = seatForm;
this.disableRightClick(canvas_id);
}
SeatingChartView.prototype.render = function() {
this.stage.update();
}
SeatingChartView.prototype.addSeat = function(newSeat) {
var newCircle = new createjs.Shape();
newCircle.graphics.beginFill("black").drawCircle(0, 0, 10);
newCircle.x = newSeat.x;
newCircle.y = newSeat.y;
newCircle.seat = newSeat;
newCircle.on('click', function(event) {
if(event.nativeEvent.button == 2) {
this.seatForm.open(event.currentTarget.seat);
}
});
newCircle.on('pressmove', this.controller.moveSeat)
this.stage.addChild(newCircle);
}
SeatingChartView.prototype.removeSeat = function(seat) {
this.stage.children.forEach(function(child) {
if(child.seat === seat) {
this.stage.removeChild(child);
}
});
}
SeatingChartView.prototype.setBackground = function(imageLocation) {
this.background = new createjs.Bitmap(imageLocation);
window.setTimeout(function() {
this.stage.canvas.width = this.background.image.width;
this.stage.canvas.height = this.background.image.height;
this.stage.addChild(this.background);
this.stage.update();
}.bind(this), 500);
}
SeatingChartView.prototype.disableRightClick = function(canvas_id) {
$(function() {
$('#' + canvas_id).bind('contextmenu', function(e) {
return false;
});
});
}
return SeatingChartView;
})();
In that case every new instance of Seat will share the newest Self object since it is set in the constructor. You should avoid doing this.
A more practical demo example might be something like this, where you want to make sure this is the instance of the class.
function Foo() {
var _this = this;
_this.someItem = {};
_this.go = function() {
doSomethingElse(function(result) {
_this.someItem.something = result; // _this and this are different
});
};
};
function doSomethingElse(callback) {
callback('asdf');
}
var foo = new Foo();
foo.go();
For your example using that pattern, you can define the _this in each method if it would be any benefit (this one wouldn't, but a more complex example might):
Seat.prototype.moveTo = function(newX, newY) {
var _this = this;
if(newX >= 0 && newY >= 0) {
_this.x = newX; _this.y = newY;
}
};
Yes, by doing it this way, all instances of Seat will have the same this, causing problems all over the place. Just remove the var self and use this in all places where you were using self. In the code you've given, there's no point where you will lose reference to this.
(#added example) Now your question makes more sense.
Instead of trying to handle this for all methods at once, you'll have to handle it at each point where you're using a function that has a different this (any function that isn't on the prototype or instance).
If you don't need this inside the callback, I would just use .bind to make the instance this available inside. Note however that .bind isn't supported in some (very)old versions of IE, so you'll either need a polyfil to work for those, or store this in a var.
SeatingChartView.prototype.addSeat = function(newSeat) {
var newCircle = new createjs.Shape();
newCircle.graphics.beginFill("black").drawCircle(0, 0, 10);
newCircle.x = newSeat.x;
newCircle.y = newSeat.y;
newCircle.seat = newSeat;
newCircle.on('click', function(event) {
if(event.nativeEvent.button == 2) {
this.seatForm.open(event.currentTarget.seat);
}
}.bind(this)); // modified here, added `.bind(this)`
newCircle.on('pressmove', this.controller.moveSeat)
this.stage.addChild(newCircle);
}
This would totally negate the purpose of "classing". But in JS it's called prototyping.
Principally you want the base prototype to be "copied" when creating new instances. The base prototype should be shielded from changes when extended.
Suppose you have done what you did, all instances of Seat will have the same properties. Even worst, when creating new "copies" of Seat, all other previously created copies will have their values changed.
Since you want this to maintain reference to Seat, I would recommend using the following pattern:
var Base = {
init: function(arg) {
this.name = arg;
},
getName: function() {
return this.name;
}
}
Base.init('foo');
Base.getName(); // returns 'foo'
Your transformed code:
var Seat = {
init: function(startX, startY, inputSeatNumber, inputTableNumber) {
this.radius = 10;
this.x = startX;
this.y = startY;
this.seatNumber = inputSeatNumber;
this.tableNumber = inputTableNumber;
},
moveTo: function(newX, newY) {
if (newX >= 0 && newY >= 0) {
this.x = newX; this.y = newY;
}
},
setBackground: function(imageLocation) {
var self = this;
this.background = new createjs.Bitmap(imageLocation);
setTimeout(function() {
self.stage.canvas.width = self.background.image.width;
self.stage.canvas.height = self.background.image.height;
self.stage.addChild(self.background);
self.stage.update();
}, 500);
}
}
Extend the prototype:
var vipSeat = Object.create(Seat);
vipSeat.init( //your init values )
You can also not create an init method and simply use Object.create's second argument to assignment initial values to the prototype: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Example:_Using_propertiesObject_argument_with_Object.create
I'm currently trying to create an HTML5 Canvas game and I want to be able to attach functions to buttons that activate when clicked. I can do this for unique functions but I'm struggling to find a way to do it when looping through many buttons with a predefined function.
I've created an example to show what I've tried so far:
jsFiddle: http://jsfiddle.net/ra1rb74w/1/
// The class that we want to create an array of
myClass = function() {
this.aFunction;
};
myClass.prototype = {
// Add a new function to this class
addFunction: function (newFunction) {
this.aFunction = newFunction;
},
// Use the current function
useFunction: function () {
if (this.aFunction != null) {
this.aFunction;
}
}
};
// The base function we will use in the classes
var baseFunction = function(x) { console.log(x); }
// Create the array of classes
var myClasses = [];
// Add 10 classes to the array and add a function to each of them
for (var x = 0; x < 10; x++) {
myClasses.push(new myClass());
myClasses[x].addFunction(baseFunction(x));
}
// Use the function in the first class
myClasses[0].useFunction();
You can see that all the functions get triggered which I don't want, and the useFunction() function doesn't work. Is there a way to do this?
So you are triggering baseFunction by calling baseFunction(x). You need to either get baseFunction to return a function which can be executed:
// The class that we want to create an array of
myClass = function() {
this.aFunction;
};
myClass.prototype = {
// Add a new function to this class
addFunction: function (newFunction) {
this.aFunction = newFunction;
},
// Use the current function
useFunction: function () {
if (typeof this.aFunction === "function") {
this.aFunction.call(this);
}
}
};
// The base function we will use in the classes
var baseFunction = function(x) {
return function() {
console.log(x);
};
}
// Create the array of classes
var myClasses = [];
// Add 10 classes to the array and add a function to each of them
for (var x = 0; x < 10; x++) {
myClasses.push(new myClass());
myClasses[x].addFunction(baseFunction);
}
// Use the function in the first class
myClasses[3].useFunction();
JsFiddle
Or add another parameter to addFunction which can be called like addFunction(baseFunction, x):
// The class that we want to create an array of
myClass = function() {
this.aFunction;
};
myClass.prototype = {
// Add a new function to this class
addFunction: function (newFunction, value) {
this.aFunction = newFunction;
this.x = value;
},
// Use the current function
useFunction: function () {
if (typeof this.aFunction === "function") {
this.aFunction.call(this, this.x);
}
}
};
// The base function we will use in the classes
var baseFunction = function(x) { console.log(x); }
// Create the array of classes
var myClasses = [];
// Add 10 classes to the array and add a function to each of them
for (var x = 0; x < 10; x++) {
myClasses.push(new myClass());
myClasses[x].addFunction(baseFunction, x);
}
// Use the function in the first class
myClasses[3].useFunction();
JsFiddle
Note I also changed your check for aFunction == null as the function passed in may be null, or a string, or anything else. You want to check if it is executable.
Change to
...
myClass.prototype = {
// Add a new function to this class
addFunction: function (newFunction, x) {
this.aFunction = newFunction;
this.aFunctionX = x;
},
useFunction: function () {
if (this.aFunction != null) {
this.aFunction(this.aFunctionX);
}
}
};
...
...
for (var x = 0; x < 10; x++) {
myClasses.push(new myClass());
myClasses[x].addFunction(baseFunction, x);
}
...
Here is a fiddle http://jsfiddle.net/ra1rb74w/6/
So I am using a function to update my values, but I can't then get them back. I see values don't get updated, but is there any way of saving them as a reference to the return of the function.
function Amphibia(wheelRadius, finsPerPropeller, propellersSpinDirection, mode) {
this.speed = 0;
this.mode = mode;
var amphibiaWheel = new PropulsionUnits.Wheel(wheelRadius);
var amphibiaPropeller = new PropulsionUnits.Propeller(finsPerPropeller, propellersSpinDirection);
this.changeMode = function () {
if (mode == "land") {
mode = "water";
}
else if(mode == "water") {
mode = "land";
}
return {
mode: mode
}
}
this.accelerate = function() {
if(this.mode == "water"){
this.speed += amphibiaPropeller.acceleration;
}
else if(this.mode == "land"){
this.speed += 4*amphibiaWheel.acceleration;
}
}
this.changePropellerSpinDirection = function() {
amphibiaPropeller.changeSpinDirection();
}
return {
speed: this.speed,
mode: this.mode,
changeMode: this.changeMode,
accelerate: this.accelerate,
changePropellerSpinDirection: this.changePropellerSpinDirection
}
}
So here I am experiencing problems with changing the mode and the changeMode function expression. Mode in it should refer to this.mode and then I should be able to update the value.
mode and this.mode are not the same. In your functions you are checking/setting values on mode and this.mode, separately.
Either should work fine, as long as you're using one or the other, in the same place, the same way.
Edit
var Amphibia = function (wheelRadius, finsPerPropeller, propellersSpinDirection, mode) {
var amphibia = this,
MODES = { LAND : "land", WATER : "water" };
amphibia.getMode = function () { return mode; };
amphibia.setMode = function (val) { mode = val; };
amphibia.changeMode = function () {
amphibia.setMode((mode === MODES.LAND) ? MODES.WATER : MODES.LAND);
};
};
var amphibia = new Amphibia("", "", "", "land");
amphibia.getMode(); // "land"
amphibia.changeMode();
amphibia.getMode(); // "water"
mode is now 100% private, and unique to that instance.
If you don't need it to be, then you can append it to this, if you'd like.
But here's your problem:
var Amphibia = function () {
var amphibia = this,
amphibiaPropeller = new Propeller( );
// mode, getMode, setMode, etc...
amphibia.accelerate = function () {
if (amphibia.getMode() === "water") {
this.speed += amphibiaPropeller.acceleration;
}
};
};
var amphibia = new Amphibia();
var bob = { speed : 0 };
bob.accelerate = amphibia.accelerate;
bob.accelerate();
// if amphibia.mode === "water", bob.speed += amphibiaPropeller.acceleration
bob.speed; // === amphibiaPropeller.acceleration
setTimeout(amphibia.accelerate, 10); // call amphibia.accelerate in ~10ms
// if amphibia.mode === "water", window.speed += amphibiaPropeller.acceleration
window.speed; // === amphibiaPropeller.acceleration
Be consistent in how you refer to things.
Don't mix self and this, unless you intend to get those side-effects...
And unless you have a very, very good reason to do so (like you're building a framework/engine, not the modules/classes of the game/simulation which use the engine; ie: the difference between building jQuery and building something with jQuery), then you should probably avoid doing it.
If you have closure ("private") state that you want to expose to the outside world, all you need is a function that returns that value, and/or one that sets it.
All of a sudden, the differences between self and this and what is which, when, all go away, as long as you are consistent with how you use them, and you know what the value of this is going to be, every time you call the method.
Notice I'm not returning anything...
When I use new, the value of this (amphibia/self) gets returned by default.
If you want to use private values, and return a "Revealing Module" (which is what I typically prefer), then you can simply do this:
var Amphibia = function (mode) {
var getMode = function () { return mode; },
setMode = function (val) { mode = val; },
changeMode = function () {
setMode( mode === "water" ? "land" : "water" );
};
return {
getMode : getMode,
setMode : setMode,
changeMode : changeMode
};
};
var amphibia = new Amphibia("water");
// `new` won't do any harm, but you can also not use it,
// without it saving everything to `window`
amphibia.getMode(); // "water"
amphibia.changeMode();
amphibia.getMode(); // "land"
Or, maybe if you want that to look a little more like a module/component...
return {
mode : { get : getMode, set : setMode, switch : changeMode }
};
var amphibia = Amphibia("land");
amphibia.mode.get(); // "land"
amphibia.mode.switch();
amphibia.mode.get(); // "water"
var bob = { };
bob.switchAmphibiaMode = amphibia.mode.switch;
bob.switchAmphibiaMode();
amphibia.mode.get(); // "land"
setTimeout(amphibia.mode.switch, 10);
setTimeout(function () { console.log(amphibia.mode.get()); }, 20);
// 10ms amphibia.mode.switch();
// 20ms console.log(amphibia.mode.get());
// > "water"
...or whatever other structure you'd like.
You don't need a this at all.
But this is something to be very, very careful with in JavaScript, because the meaning of this changes every time you call a function, and if half of the code uses this and half uses self, you're bound for some surprises.
I managed to find the answer myself! :) So basicly this in the function constructor refers to Amphibia and this in the this.changeMode function expression refers to object window. Therefore we can define a variable self = this; in the constructor, so we can refer to the same thing in the function expression, as in the function. I explained it a bit awful, but here is my fixed code ;)
function Amphibia(wheelRadius, finsPerPropeller, propellersSpinDirection, mode) {
this.speed = 0;
var self = this;
self.mode = mode;
var amphibiaWheel = new PropulsionUnits.Wheel(wheelRadius);
var amphibiaPropeller = new PropulsionUnits.Propeller(finsPerPropeller, propellersSpinDirection);
this.changeMode = function () {
if (self.mode == "land") {
self.mode = "water";
}
else if(self.mode == "water") {
self.mode = "land";
}
}
this.accelerate = function() {
if(self.mode == "water"){
this.speed += amphibiaPropeller.acceleration;
}
else if(self.mode == "land"){
this.speed += 4*amphibiaWheel.acceleration;
}
}
this.changePropellerSpinDirection = function() {
amphibiaPropeller.changeSpinDirection();
}
return {
speed: this.speed,
mode: this.mode,
changeMode: self.changeMode,
accelerate: this.accelerate,
changePropellerSpinDirection: this.changePropellerSpinDirection
}
}
Here is a jsFiddle: http://jsfiddle.net/hb2pE/
I have a constructor function inside a module pattern. I create 2 instances of this module using this code:
var jo = new sliderJS("mySliderJS")
jo.start()
var jow = new sliderJS("mySliderJS2")
jow.start()
This is the module sliderJS:
var sliderJS = function($){
var sliderJS,
sliderJSslide,
sliderJSslideLength,
sliderJSprev,
sliderJSnext,
slideEvents,
sliderID
slideEvents = {
doSlide: function(type, e){
if(e){e.preventDefault()}
for(var i = 0; i < sliderJSslideLength; i++){
var slideIsActive = hasClass("active", sliderJSslide[i])
if(slideIsActive){
sliderJSslide[i].className = "sliderJS-slide"
switch (type) {
case "next":
slideEvents.moveSlideNext(i)
break;
case "prev":
slideEvents.moveSlidePrev(i)
break;
}
break;
}
}
},
moveSlideNext: function(i){
if(i+1 < sliderJSslide.length){
sliderJSslide[i+1].className = "sliderJS-slide active"
} else {
sliderJSslide[0].className = "sliderJS-slide active"
}
},
moveSlidePrev: function(i){
if(i-1 != -1){
sliderJSslide[i-1].className = "sliderJS-slide active"
} else {
sliderJSslide[sliderJSslide.length -1].className = "sliderJS-slide active"
}
}
}
function start(id){
setVariables(id)
setEventListeners()
}
function setVariables(id){
//sliderID = id
sliderJS = $("#" + sliderID)[0]
sliderJSslide = $("#" + sliderID + " > .sliderJS-slide")
sliderJSslideLength = sliderJSslide.length
sliderJSprev = $("#" + sliderID + "-prev")[0]
sliderJSnext = $("#" + sliderID + "-next")[0]
}
function setEventListeners(){
sliderJSnext.addEventListener("click", function(){return function(e){ slideEvents.doSlide("next", e)}}())
sliderJSprev.addEventListener("click", function(){return function(e){ slideEvents.doSlide("prev", e)}}())
}
function hasClass(parClass, parElement){
if(parElement.className.indexOf(parClass) == -1){return false} else {return true}
}
return function constr(id){
sliderID = id
this.start = start
}
}(Sizzle)
If i'm using only 1 instance than the code works. As soon as i create a second instance the slider for the first one stops working because all the variables values have been changed to reflect the second slider.
What am i doing wrong?
Here is that jsFiddle again (remove the last instance -var jow- to see the script actually working): http://jsfiddle.net/hb2pE/
The module is a little bit messed up. It should follow this pattern:
var sliderJS = function(sliderID) {
//define private functions here eg. setEventListeners
//Return an object containing functions that are public eg. doSlide
return {
doSlide: function(type, e){
}
};
};
So you don't need the new keyword, just call:
var slider1 = sliderJS("mySliderJS2");
If, however, you're creating a lot of instances and would prefer better performance, you'd be better off creating a function and populating its prototype - then you can call that function with the new keyword.