This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 5 years ago.
So i'm trying to make a "game" with javascipt witc has rectangles that go arround the canvas and they have timers. When rectangles timer goes above certain limit rectangle gets a new direction.
I'm trying to make the rectangles with class so i can have multiple of them running at same time. But the problem is that i cant get the functions inside of the class pass data to eachother (i think that's the problem...).
On the draw function this.cubeLocX is not defined and this.ctx.fillStyle="#2a2b2d"; gets error Cannot set property 'fillStyle' of undefined
class cubeV3 {
constructor(canvas) {
//var canvas = document.getElementById('canvas');
this.canvas = canvas;
this.cubeLocX = 100;
this.cubeLocY = 0;
this.speed = getRandomNumber();
this.dir = 0;
this.turnTimer = 0;
this.turnTimerMax = getRandomNumberV2(30,100);
this.ctx = this.canvas.getContext("2d");
}
testClass() {
alert('This is CubeV3 classes testfunction reporting in!');
/*you have put the call to the private method inside the constructor of the javascript class. in that point the functions are not yet initialized
but if you initialize the object like so:
var test = new MyObject();
and then do this:
test.myMethod();
it will work.*/
}
update() {
this.turnTimer ++;
if (this.turnTimer > this.turnTimerMax) {
this.dir = this.turnTimerMax = getRandomNumberV2(1,4);
this.turnTimer = 0;
this.turnTimerMax = getRandomNumberV2(30,100);
}
if (this.dir == 1) {this.cubeLocY -=this.speed;}//UP
if (this.dir == 2) {this.cubeLocY +=this.speed;}//DOWN
if (this.dir == 3) {this.cubeLocX -=this.speed;}//LEFT
if (this.dir == 4) {this.cubeLocX +=this.speed;}//RIGHT
}
draw(self,) {
//this.cubeLocX = 555;
resetCanvas();
console.log(this.cubeLocX);
alert(this.cubeLocX);
this.ctx.fillStyle="#2a2b2d";
this.ctx.fillRect(this.cube_loc_x,this.cube_loc_y,25,25);
}
}
So how can i make my class work?
EDIT calling the class
This is placed after the draw for calling and updating the class
//setInterval(resetCanvas,10)
var testCube = new cubeV3(canvas);
setInterval(cube, 10);
//setInterval(testCube, 10);
setInterval(testCube.update, 10);
setInterval(testCube.draw, 10);
Advice 1: "this" can be tricky in js, avoid it if it is possible.
Advice 2: maybe it is not the best Oop design to define the draw function in the rectangle class. It should be better to define a standalone draw function which has a shape argument:
function draw(shape){
shape.ctx.fillStyle = ...
...
}
This way you can avoid some headache in the future also.
Related
This question already has answers here:
Why won't this Javascript method keep calling itself?
(4 answers)
Closed 7 years ago.
I try to make an object that move, resize and change the color of a div randomly every second.
In my classical HTML page there is
<div id="Rectangle1"></div>
Here is my code
var colors = ["red","yellow","blue","dark","green","pink","purple"];
function Rectangle(tag){
this.moveNShape = function() {
//Defining new values
this.width = Math.floor(Math.random()*250)+50;
this.height = Math.floor(Math.random()*250)+50;
this.x = Math.floor(Math.random()*400)+100;
this.y = Math.floor(Math.random()*400)+100;
this.color = colors[Math.floor(Math.random()*7)];
//Update the view
this.tag.css("position","absolute").css("height",this.height).css("width",this.width).css("left",this.x).css("top",this.y);
this.tag.css("backgroundColor",this.color);
//Launch again
setTimeout(this.moveNShape,1000);
}
this.tag = $('#'+tag);
this.moveNShape();
}
var rect1 = new Rectangle("Rectangle1");
It works one time and after I get the error "Cannot read property 'css' of undefined". I tried to rewrite it in many ways but I can't find a solution.
Can you explain my mistake ?
Thank you =)
Use bind to set the this variable to the Rectangle object.
The reason you were getting that error before is when moveNShape was called via the setTimeout, this became window because the execution context had changed
var colors = ["red","yellow","blue","dark","green","pink","purple"];
function Rectangle(tag){
this.moveNShape = function() {
//Defining new values
this.width = Math.floor(Math.random()*250)+50;
this.height = Math.floor(Math.random()*250)+50;
this.x = Math.floor(Math.random()*400)+100;
this.y = Math.floor(Math.random()*400)+100;
this.color = colors[Math.floor(Math.random()*7)];
//Update the view
this.tag.css("position","absolute").css("height",this.height).css("width",this.width).css("left",this.x).css("top",this.y);
this.tag.css("backgroundColor",this.color);
//Launch again
setTimeout(this.moveNShape.bind(this),1000);
}
this.tag = $('#'+tag);
this.moveNShape();
}
var rect1 = new Rectangle("Rectangle1");
I'm trying to scroll a greensock tween in pixi. I'm getting errors trying to hook the code that gets the mouse/arrow input (trackpad.value) with my tween.
Here's my working greensock test tween, to make sure I have greensock working in pixi: (have to tween the position element in pixi):
var t1 = new TimelineMax({onUpdate:animate, onUpdateScope:stage});
t1.to(bg.position, 3, {y:100});
Here's my code where I'm trying to hook trackpad.value into the greensock code (I'm getting the following error: Uncaught TypeError: bg.position is not a function):
trackpad = new Trackpad(document);
var t1 = new TimelineMax({paused:true, onUpdate:animate, onUpdateScope:stage});
t1.progress(bg.position( Math.abs( trackpad.value ) / 3240));
I then tried the following - it didn't work (but I didn't get an error):
var moveIt = trackpad.value / 3240;
t1.progress(bg.position, moveIt, {});
Here's the code where the trackpad value is defined:
/*
* param: the html element that will be scrolled
*/
Trackpad = function(target)
{
this.target = target;
this.value = 0;
this.easingValue = 00;
this.dragOffset = 0;
this.dragging;
this.speed= 0;
this.prevPosition = 0;
$(this.target).mousedown($.proxy(this.onMouseDown, this));
this.target.onmousewheel = $.proxy(this.onMouseWheel, this);
// not forgetting touchs!
this.target.ontouchstart = $.proxy(this.onTouchStart, this);
// stop dragging!
$(document).keydown( $.proxy(this.onArrow, this))//function(e){
//this.target.ondragstart = function(){return false;}
}
// set constructor
Trackpad.constructor = Trackpad;
// create the functions
Trackpad.prototype.unlock = function()
{
this.locked = false;
this.speed = 0;
this.easingValue = this.value;
}
Trackpad.prototype.lock = function()
{
this.locked = true;
}
Trackpad.prototype.update = function()
{
if(this.easingValue > 0)this.easingValue = 0;
if(this.easingValue < -10700)this.easingValue = -10700;
this.value = this.easingValue;
if(this.dragging)
{
var newSpeed = this.easingValue - this.prevPosition;
newSpeed *= 0.7;
this.speed += (newSpeed - this.speed) *0.5;//+= (newSpeed - this.speed) * 0.5;
this.prevPosition = this.easingValue;
}
else
{
this.speed *= 0.9;
this.easingValue += this.speed;
if(Math.abs(this.speed) < 1)this.speed = 0;
}
}
Trackpad.prototype.onArrow = function(event)
{
if (event.keyCode == 38) {
// UP
this.speed = 4;
return false;
}
else if (event.keyCode == 40) {
// UP
this.speed -= 4
return false;
}
}
Trackpad.prototype.onMouseWheel = function(event)
{
event.preventDefault();
this.speed = event.wheelDelta * 0.1;
}
Trackpad.prototype.startDrag = function(newPosition)
{
if(this.locked)return;
this.dragging = true;
this.dragOffset = newPosition - this.value;
}
Trackpad.prototype.endDrag = function(newPosition)
{
if(this.locked)return;
this.dragging = false;
}
Trackpad.prototype.updateDrag = function(newPosition)
{
if(this.locked)return;
this.easingValue = (newPosition - this.dragOffset);
}
/*
* MOUSE
*/
Trackpad.prototype.onMouseDown = function(event)
{
if(event)event.preventDefault();
event.returnValue = false;
$(document).mousemove($.proxy(this.onMouseMove, this));
$(document).mouseup($.proxy(this.onMouseUp, this));
this.startDrag(event.pageY);
}
Trackpad.prototype.onMouseMove = function(event)
{
if(event)event.preventDefault();
this.updateDrag(event.pageY);
}
Trackpad.prototype.onMouseUp = function(event)
{
//$(this.target).mousemove(null);
$(document).unbind('mousemove');
$(document).unbind('mouseup');
//this.target.onmousemove = null;
this.endDrag();// = false;
}
/*
* TOUCH!
*/
Trackpad.prototype.onTouchStart = function(event)
{
//event.preventDefault();
this.target.ontouchmove = $.proxy(this.onTouchMove, this);
this.target.ontouchend = $.proxy(this.onTouchEnd, this);
this.startDrag(event.touches[0].clientY);
}
Trackpad.prototype.onTouchMove = function(event)
{
event.preventDefault();
this.updateDrag(event.touches[0].clientY);
}
Trackpad.prototype.onTouchEnd = function(event)
{
this.target.ontouchmove = null;
this.target.ontouchend = null;
this.endDrag();
}
** edit
tl = new TimelineLite( { paused: true } );
// respond to scroll event - in this case using jquery
$(window).scroll();
//apply whatever math makes the most sense to progress the timeline progress from 0 to 1 within those parameters. Something like,
$(window).scroll( function() {
var st = $(this).scrollTop();
if ( st < someArbitraryValue ) { // someArbitraryValue, where to start
// Here, "someOtherArbitaryValue" would be the
// "height" of the scroll to react to
tl.progress( Math.abs( st ) / someOtherArbitaryValue );
}
});
Is this the kind of effect you were after?
JavaScript:
window.requestAnimFrame=(function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(callback){window.setTimeout(callback,1000/60);};})(); //http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
var stageWidth=$(window).innerWidth();
var stageHeight=$(window).innerHeight();
var renderer=PIXI.autoDetectRenderer(stageWidth,stageHeight);
var bg,cat,moon,blue,trackpad,texture1,texture2,texture3;
document.body.appendChild(renderer.view);
texture1=PIXI.Texture.fromImage('https://dl.dropboxusercontent.com/u/45891870/Experiments/StackOverflow/1.5/cat.jpg');
texture2=PIXI.Texture.fromImage('https://dl.dropboxusercontent.com/u/45891870/Experiments/StackOverflow/1.5/moon.jpg');
texture3=PIXI.Texture.fromImage('https://dl.dropboxusercontent.com/u/45891870/Experiments/StackOverflow/1.5/blue.jpg');
bg=new PIXI.Container();
cat=new PIXI.Sprite(texture1);
moon=new PIXI.Sprite(texture2);
blue=new PIXI.Sprite(texture3);
cat.anchor.x=cat.anchor.y=moon.anchor.x=moon.anchor.y=blue.anchor.x=blue.anchor.y=0;
cat.position.x=cat.position.y=moon.position.x=blue.position.x=bg.position.x=bg.position.y=0;
cat.width=moon.width=blue.width=stageWidth;
moon.position.y=1080;
blue.position.y=2160;
bg.addChild(cat);
bg.addChild(blue);
bg.addChild(moon);
bg.vy=bg.vx=0;//what are those?
trackpad=new Trackpad(document);
requestAnimFrame(animate);
function animate(){
requestAnimFrame(animate);
bg.position.y=trackpad.value;
trackpad.update();
renderer.render(bg);
}
Let me know if this is exactly the thing you were looking for & I'll then break it down for you in terms of what has changed in comparison to your code.
Notes:
First & foremost, I have used the latest version (v3.0.6) of Pixi.JS in my example above. This v3 update brought a few major changes. Couple of them prominent to your problem are:
No need for Stage object anymore for rendering purposes. Any Container type object can be used directly to be rendered on canvas.
Shortening of the name DisplayObjectContainer to simply Container. This is probably the reason why you are getting the error when trying to implement my code in your environment that you mentioned in comments because I presume you are using one of the old verions.
Read all about this update here, here & here.
I always prefer to use the latest & greatest of GSAP (v1.17.0). Even the dot releases of this framework brings major updates which is why I like to keep it up to date. Read an important note on this update here. Having said that, the current implementation doesn't really use TweenMax at all.
TweenMax bundles EasePack, CSSPlugin & a few other things. No need to load them in separately. Update your HTML accordingly. Use this handy GSAP CheatSheet by Peter Tichy to get such information and more about this tool.
Changes in Trackpad.js:
Inside the update method, there was a maximum scroll limit defined the page can scroll up to. That value previously was -10700. I changed it to -2160. You may want to set it to -3240 I think, based on what I have been able to understand so far as to what you are trying to achieve.
Formatting changes.
Changes in main.js (whatever name you gave to your main script file):
Added a requestAnimationFrame polyfill thanks to Paul Irish.
Removed the var stage= new PIXI.Stage(0xff00ff); line. Read #1 above for details.
Renamed DisplayObjectContainer to Container which was assigned to bg. Read #1 above for details.
Added bg.position.y=trackpad.value; in the animate loop. You were missing this. You will need to use trackpad.value in order to position your bg.
Added trackpad.update(); in the same animate loop. This is the big one and IMHO, this is the one you were failing to understand the purpose of. In summary, Trackpad.js needs to update its value on a timely basis & the only loop you have got running is the animate loop thanks to requestAnimFrame. Hence, the update(); method is called.
Rendering bg instead of stage. Read #1 above for details.
Formatting changes.
Let me know if anything is unclear.
T
I thought of editing the old answer but decided against it because I think it answers your original question.
Take a look at this Codepen demo for a new approach to the same problem. I am really hoping to listen to community on the approach I have taken here in terms of listening to events and using them to adjust a GSAP timeline.
There are 4 JS files used in my example: app.js, constants.js, timeline.js & listeners.js. Links to which can be found in the settings gear icon of the JavaScript editor of the demo. All of these files are heavily annotated with links to solutions I found over the internet to specific problems.
Among these files, code of app.js is as follows:
JavaScript:
function Application(){}
Application.prototype.init=function(){
this.constants=Constants.getInstance();
this.BASE_URL=this.constants.BASE_URL;
this.IMAGE_JS_URL=this.constants.IMAGE_JS_URL;
this.IMAGE_PIXI_URL=this.constants.IMAGE_PIXI_URL;
this.IMAGE_GSAP_URL=this.constants.IMAGE_GSAP_URL;
this.createPolyfillForBind();
this.setupRenderer();
this.loadImages();
};
Application.prototype.setupRenderer=function(){
this.stageWidth=window.innerWidth;
this.stageHeight=window.innerHeight;
//this.renderer=PIXI.autoDetectRenderer(this.stageWidth,this.stageHeight);
this.renderer=new PIXI.CanvasRenderer(this.stageWidth,this.stageHeight);
document.body.appendChild(this.renderer.view);
};
Application.prototype.loadImages=function(){
var self=this;
this.loader=new PIXI.loaders.Loader(this.BASE_URL,1,{crossOrigin:''}); // PIXI Loader class [http://pixijs.github.io/docs/PIXI.loaders.Loader.html]
this.loader.add(this.IMAGE_JS_URL); // Loader extends ResourceLoader [http://adireddy.github.io/docs/haxe-pixi/v3/types/pixi/plugins/resourceloader/ResourceLoader.html]
this.loader.add(this.IMAGE_PIXI_URL);
this.loader.add(this.IMAGE_GSAP_URL);
//this.loader.once('complete',function(){self.onImagesLoaded.apply(self);}); // Vanilla JS alternative to jQuery's proxy() method [http://stackoverflow.com/a/4986536]
this.loader.once('complete',this.onImagesLoaded.bind(this)); // bind() polyfill [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill]
this.loader.load();
};
Application.prototype.onImagesLoaded=function(){
this.setupSprites();
this.initTimeline();
this.initListeners();
this.startTicker();
};
Application.prototype.setupSprites=function(){
this.containerBg=new PIXI.Container();
this.spriteJS=new PIXI.Sprite(PIXI.utils.TextureCache[this.BASE_URL+this.IMAGE_JS_URL]); // TextureCache in action [http://www.html5gamedevs.com/topic/7674-load-textures-synchronously/?p=45836]
this.spritePIXI=new PIXI.Sprite(PIXI.utils.TextureCache[this.BASE_URL+this.IMAGE_PIXI_URL]); // PIXI.TextureCache became PIXI.utils.TextureCache in v3 [http://www.html5gamedevs.com/topic/14144-v3-utilstexturecache-utils-is-not-defined/?p=80524]
this.spriteGSAP=new PIXI.Sprite(PIXI.utils.TextureCache[this.BASE_URL+this.IMAGE_GSAP_URL]);
this.containerBg.addChild(this.spriteJS);
this.containerBg.addChild(this.spritePIXI);
this.containerBg.addChild(this.spriteGSAP);
this.spriteJS.anchor.x=this.spriteJS.anchor.y=this.spritePIXI.anchor.x=this.spritePIXI.anchor.y=this.spriteGSAP.anchor.x=this.spriteGSAP.anchor.y=0;
this.spriteJS.position.x=this.spriteJS.position.y=this.spritePIXI.position.x=this.spriteGSAP.position.x=this.containerBg.position.x=this.containerBg.position.y=0;
this.scaleImage(this.spriteJS);
this.scaleImage(this.spritePIXI);
this.scaleImage(this.spriteGSAP);
this.spritePIXI.alpha=this.spriteGSAP.alpha=0;
this.spriteJS.position.y=this.constants.GUTTER;
this.spritePIXI.position.y=this.spriteJS.height*2+this.constants.GUTTER;
this.spriteGSAP.position.y=this.spriteJS.height+this.spritePIXI.height*2+this.constants.GUTTER;
};
Application.prototype.scaleImage=function(sprite){
//var scale=Math.min(this.stageWidth/sprite.width,this.stageHeight/sprite.height); // resize with aspect ratio [http://community.createjs.com/discussions/createjs/547-resizing-canvas-and-its-content-proportionally-cross-platform#comment_27266530] and [https://opensourcehacker.com/2011/12/01/calculate-aspect-ratio-conserving-resize-for-images-in-javascript/]
var scale=this.stageWidth/sprite.width;
sprite.scale.x=sprite.scale.y=scale;
};
Application.prototype.initTimeline=function(){
this.timeline=new Timeline();
this.timeline.init(this.containerBg,this.spriteJS,this.spritePIXI,this.spriteGSAP,this.stageWidth,this.stageHeight);
};
Application.prototype.initListeners=function(){
var self=this;
//this.listeners=new Listeners();
//this.constants.setListenersObject(this.listeners);
//this.listeners.init();
this.listeners=Listeners.getInstance();
this.listeners.addListeners();
document.addEventListener(this.constants.SCROLLED,this.onScroll.bind(this),false);
document.addEventListener(this.constants.STARTED_DRAG,this.onStartDrag.bind(this),false);
document.addEventListener(this.constants.DRAGGED,this.onDrag.bind(this),false);
document.addEventListener(this.constants.END_DRAG,this.onEndDrag.bind(this),false);
};
Application.prototype.onScroll=function(e){ this.timeline.onScroll(e); };
Application.prototype.onStartDrag=function(e){ this.timeline.onStartDrag(e); };
Application.prototype.onDrag=function(e){ this.timeline.onDrag(e); };
Application.prototype.onEndDrag=function(e){ this.timeline.onEndDrag(e); };
Application.prototype.startTicker=function(){
var self=this;
//TweenLite.ticker.addEventListener('tick',function(){self.render.apply(self);},false); // Vanilla JS alternative to jQuery's proxy() method [http://stackoverflow.com/a/4986536]
TweenLite.ticker.addEventListener('tick',this.render.bind(this),false);
};
Application.prototype.render=function(){this.renderer.render(this.containerBg);};
Application.prototype.createPolyfillForBind=function(){ // [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill]
if(!Function.prototype.bind){
Function.prototype.bind=function(oThis){
if(typeof this!=='function'){
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs=Array.prototype.slice.call(arguments,1),
fToBind=this,
fNOP=function(){},
fBound=function(){
return fToBind.apply(this instanceof fNOP
?this
:oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype=this.prototype;
fBound.prototype=new fNOP();
return fBound;
};
}
};
//
var app=new Application();
app.init();
P.S. I have also heavily experimented with design patterns in this same example, mainly Prototype and Singleton patterns. I am also looking forward to comments on them as well from the community.
T
This question already has answers here:
JavaScript - Owner of "this"
(6 answers)
Closed 8 years ago.
I have this function right here which takes the parts of my object and illuminates them successively with a delay (it switches the opacity of an element to 1 and then switches the previous element opacity to 0 to illuminate them successively).
The problem is that I cannot use the this keyword to access the parent objects parts in the illuminateInternal function.
This hinders any possible reuse of my object since I will have maleBackNoZoom and maleBackFullZoom objects.
I don't want to change the variable names of the illuminateInternal function when I re-use my object so I would like to use something like the this keyword in the illuminateInternal function as well.
maleBackNoZoom = {
parts:{
armsRight: findItemById("29") ,
armsLeft: findItemById("28"),
legsRight: findItemById("21"),
legsLeft: findItemById("22"),
back: findItemById("24"),
buttocks: findItemById("23"),
neck: findItemById("26"),
head: findItemById("27")
},
illuminate: function() {
var propertiesToIlluminate = [], prop, illuminateInternal, i = 0, delay = 200, intervalId;
for (prop in this.parts) {
propertiesToIlluminate.push(prop);
}
illuminateInternal = function () {
var property = propertiesToIlluminate[i];
var previousProperty = propertiesToIlluminate[i-1];
maleBackNoZoom.parts[property].opacity = 1;
console.log(previousProperty);
if (typeof previousProperty !== 'undefined'){
maleBackNoZoom.parts[previousProperty].opacity = 0;
}
paper.view.update();
i++;
if (i === propertiesToIlluminate.length) {
maleBackNoZoom.parts[property].opacity = 0;
clearInterval(intervalId);
setInterval(function(){paper.view.update()},delay);
}
};
intervalId = setInterval(illuminateInternal, delay);
}
}
You can define local variable inside illuminate method which will store reference to its object. And then you can use it as alias of this.
var self = this;
illuminateInternal = function () {
..
self.parts[property].opacity = 1;
}
This question already has answers here:
How to avoid global variables in JavaScript?
(13 answers)
Closed 9 years ago.
Im kind of a newbie on javascript still, I would say, so I think that the ways I work on projects now are kind of shaping my way of thinking about how things should be done in js. I have understood that one should work preferably with modules when programming in js. This has led me to think about events in js... For example say I have this object Monkey
function Monkey(color, position){
this.color = color;
this.position = position;
this.jumpAround = function() {
/* jumps around and screams */
};
}
And say I have built a whole app like this, with monkeys, giraffes and shih tzus, that interact in a webapp. How should the events then be handled? Are you left to just implement callbackfunctions in a global namespace? Like:
//objects
var monkey = new Monkey(brown, pos);
var giraffe = new Giraffe(yellow, pos);
var shih_tzu = new Shih_tzu(white, pos);
//event handlers
this_and_that.onclick = function() { /* this and that happens */ }
...
And then include this file in the html header? Maybe this is a silly question with an obvious answer, but still too me it seems as if there aren´t any good best practices...
You can put all your code within anonymous self-invoking function, this will also create closure:
(function(){
// create all your objects and define all events handlers here
})();
Then your code will not pollute global namespace and will be inaccessible from outside. All your event handlers will be executed within the closure.
(One side note: you can find this also in jQuery library. At the very end of the script file is the jQuery object exposed to outer world: window.jQuery = window.$ = jQuery;)
Not entirely sure I understand the question, but if you mean handling the overwriting of events in javascript caused by repeat elem.onclick = function() {} I use this function:
function addEvent(obj,event,func)
{
if(typeof func !== 'function')
{
return false;
}
if(typeof obj.addEventListener == 'function' || typeof obj.addEventListener == 'object')
{
return obj.addEventListener(event.replace(/^on/,''), func, false);
}
else if(typeof obj.attachEvent == 'function' || typeof obj.attachEvent == 'object')
{
return obj.attachEvent(event,func);
}
}
addEvent(this_and_that,'onclick',function(e) {
//do stuff
});
Here's a fleshed out example for you with object inheritance. http://jsfiddle.net/H4jqF/
CSS - just a base animal object, with a .5 second smooth transition, for when properties change (for example to make our animal JUMP).
.animal {
position: absolute;
transition: all 0.5s ease;
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
}
JavaScript
// simple HTML animal - parent class
function Animal(color, position) {
this.color = color;
this.position = position;
this.elm = document.createElement("IMG");
this.elm.className = "animal";
this.elm.style.backgroundColor = this.color;
this.update = function() {
// update the the HTML element
this.elm.style.left = this.position.x + "px";
this.elm.style.top = this.position.y + "px";
};
this.jump = function(height) {
// cheesy jump animation
// we'll use a CSS transition to smooth it out
this.position.y -= height;
this.update();
// hard code it to come back down in 500ms
// .bind(this) is used to scope the function to this instance
window.setTimeout(function() {
this.position.y += height;
this.update();
}.bind(this), 500);
};
this.update();
}
// our subclass Dog
function Dog(color, position) {
// inherit all of the parent objects properties and methods
Animal.apply(this, arguments);
// finish setup of the element
this.elm.src = "dog.png";
document.body.appendChild(this.elm);
// add our onclick event that will fire jump
this.elm.onclick = function() {
this.jump(100);
}.bind(this);
}
// spawn some dogs
new Dog("red", {x: 50, y: 200});
new Dog("blue", {x: 200, y: 200});
Hey I was wondering how I could add on to my function draw(); draw is used in my canvas engine to update everything within it. What I want to do is create a standalone engine that could say be left un-edited and yet update completely new things just linked to it. For example-
function draw(){
gameloop();
cameraWrapper();
context2D.clearRect(0,0,canvas.width, canvas.height);
}
Now say I create a new app and use this engine.. I want to be able to just create a standalone file linked to the engine say a player object.
player = new object();
function playerupdate(){
stuff;
stuff;
}
Now how could I say add the playerupdate() function into the engine.js's draw() function without editing the engine.js file? would this be like a prototype? if so and even if its not and example would be greatly appreciated! If you have any questions please ask, thanks in advance!
I think inheritance is a bit too complex for this... you can achieve all of what you want with just hooks.
Try something like this:
var postDrawHooks = [];
var draw = function(){
// do stuff
postDrawHooks.forEach(function(hook){hook()});
}
var playerUpdate = function(){...};
postDrawHooks.push(playerUpdate);
Basicaly it is a prototype. If you do not wish to complicate yourself with prototypeing you can use a "home made" inheritance:
Function.prototype.method = function(name, func) {
this.prototype[name] = func;
return this;
};
Function.method('inherits', function(parent) {
var d = {}, p = (this.prototype = new parent());
this.method('uber', function uber(name) {
if(!( name in d)) {
d[name] = 0;
}
var f, r, t = d[name], v = parent.prototype;
if(t) {
while(t) {
v = v.constructor.prototype;
t -= 1;
}
f = v[name];
} else {
f = p[name];
if(f == this[name]) {
f = v[name];
}
}
d[name] += 1;
r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
d[name] -= 1;
return r;
});
return this;
});
Now for a "class" (there is no such thing in js but this is the correct term ) you can make it inherit another "class" by using myCls.inherits(parentCls)
is there a reason you couldn't trigger events for these framework actions? that way anything listening for a 'draw' event could just hook their logic in that way? if not actual eventing, something like the hooks suggested by #sudhir jonathan would work, though i would suggest creating a method to register generic hooks, this way you could call something like
game.register('draw',myFunctionReference);
and in game object
register : function (hook,func) {
registeredHooks.push({'name' : hook, 'callback': func});
}
and in draw:
function draw(){
gameloop();
cameraWrapper();
context2D.clearRect(0,0,canvas.width, canvas.height);
for (i=0; i < registeredHooks.length; i++) {
var hook = registeredHooks[i];
if (hook.name == 'draw') hook.callback();
}
}