Issue with scoping, when calling a function in a callback - javascript

when clicking inside the canvas it will generate a ball and move to the clicked location
when the ball get's to its location I want it to remove itself. But i think i have a problem
with the scope when calling the removeBall() function.
You can find a working example her: jsfiddle
/*
* Main app logic
*/
function Main() {
this.canvas = "canvas";
this.stage = null;
this.WIDTH = 0;
this.HEIGHT = 0;
this.init();
}
Main.prototype.init = function() {
console.clear();
this.stage = new createjs.Stage(this.canvas);
this.resize();
//start game loop
createjs.Ticker.setFPS(30);
createjs.Ticker.addEventListener("tick", this.gameLoop);
//click event handler
this.stage.on("stagemousedown", function(evt) {
main.fireBall(evt);
});
};
Main.prototype.fireBall = function(evt) {
var bal = new Bal(evt.stageX, evt.stageY);
};
Main.prototype.resize = function() {
//resize the canvas to take max width
this.WIDTH = window.innerWidth;
this.HEIGHT = Math.floor(window.innerWidth * 9 / 16);
this.stage.canvas.width = this.WIDTH;
this.stage.canvas.height = this.HEIGHT;
};
Main.prototype.gameLoop = function() {
//game loop
main.stage.update();
};
/*
* Ball logic
*/
function Bal(toX, toY) {
this.toX = toX ;
this.toY = toY;
this.widthPerc = 8;
this.init();
}
Bal.prototype.width = function() {
return Math.floor(main.stage.canvas.width / 100 * this.widthPerc);
};
Bal.prototype.init = function() {
//create a new ball
this.ball = new createjs.Shape();
this.ball.graphics.beginFill("green").drawCircle(0, 0, this.width());
this.ball.x = (main.stage.canvas.width / 2) - (this.width() / 2);
this.ball.y = main.stage.canvas.height - 20;
main.stage.addChild(this.ball);
this.move();
};
Bal.prototype.move = function() {
//create a tween to cliked coordinates
createjs.Tween.get(this.ball).to({
x: this.toX ,
y: this.toY ,
scaleX:0.4,scaleY:0.4,
rotation: 180
},
750, //speed
createjs.Ease.none
).call(this.removeBall); // <---- How can i pass the correct scope to the called function?
};
Bal.prototype.removeBall = function() {
//try to remove the ball
main.stage.removeChild(this.ball);
};
var main = new Main();

The solution above using bind works, however there is a much better solution. Bind is not available in all browsers (most notably Safari 5.1, which is a modern browser). http://kangax.github.io/es5-compat-table/#Function.prototype.bind
TweenJS has built-in support for scoping functions when using call(). Just pass the scope as the 3rd argument.
Ball.prototype.move = function() {
console.log(this.toX +","+this.toY);
createjs.Tween.get(this.ball).to({
x: this.toX ,
y: this.toY ,
scaleX:0.4,scaleY:0.4,
rotation: 180
},
750, //speed
createjs.Ease.none
).call(this.removeBall, null, this);
};
You can also pass an array of function arguments as the second parameter.
Tween.call(this.removeBall, [this.ball], this);

Ok found a solution! using the bind() functionality.
Bal.prototype.move = function() {
console.log(this.toX +","+this.toY);
createjs.Tween.get(this.ball).to({
x: this.toX ,
y: this.toY ,
scaleX:0.4,scaleY:0.4,
rotation: 180
},
750, //speed
createjs.Ease.none
).call(this.removeBall.bind(this));
};

Related

Why do I get "Cannot read property...of undefined with this object?

Could someone help me figure out what the problem is. I get the error "TypeError: Cannot read property 'draw_map' of undefined(…)" in browser when running the code below:
$(document).ready(function() {
Maps = function(id, img_src, width, height) {
var self = {
id: id,
img: new Image(),
width: width,
height: height
}
self.img.src = img_src;
self.draw_map = function() {
ctx.drawImage(self.img, 0, 0, self.img.width, self.img.height, 100, 100, self.img.width * 2, self.img.height * 2);
}
}
function update_canvas() {
current_map.draw_map();
}
///////////////////////////////////////////
// get context to draw on canvas
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// Load Image for map
var current_map = Maps("field", "img/dirt.jpeg", 120, 480);
// Drawing
setInterval(update_canvas, 500);
}); // End of Document.ready
Probably because the function
Maps = function(...) { ... }
doesn't return self object.
Probably you want to use it as a construction function so change this line using new operator:
var current_map = new Maps("field", "img/dirt.jpeg", 120, 480);
Here is a working code (with the HTML elements commented out). The problem was that you are not using the this reference inside the function constructor and then not using the new operator to create an instance.
$(document).ready(function() {
Maps = function(id, img_src, width, height) {
this.id = id;
this.width = width;
this.height = height;
this.img = new Image() // replace with your Image
this.img.src = img_src;
var self = this; // Required for the draw_map function
this.draw_map = function() {
console.log("Draw image", self.img.width, self.img.height);
// Uncomment in your code: ctx.drawImage(self.img, 0, 0, self.img.width, self.img.height, 100, 100, self.img.width * 2, self.img.height * 2);
}
}
function update_canvas() {
current_map.draw_map();
}
///////////////////////////////////////////
// get context to draw on canvas. Uncomment
//var canvas = document.getElementById("canvas");
//var ctx = canvas.getContext("2d");
// Load Image for map
var current_map = new Maps("field", "img/dirt.jpeg", 120, 480);
// Drawing
setInterval(update_canvas, 500);
}); // End of Document.ready
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Paper.js - convert `paperscript` to `javascript`

I am trying to change this paperscript:
<script type="text/paperscript" canvas="canvas-1">
tool.minDistance = 10;
tool.maxDistance = 45;
var path;
function onMouseDown(event) {
path = new Path();
path.fillColor = new Color({ hue: Math.random() * 360, saturation: 1, brightness: 1 });
path.add(event.point);
}
function onMouseDrag(event) {
var step = event.delta / 2;
step.angle += 90;
var top = event.middlePoint + step;
var bottom = event.middlePoint - step;
path.add(top);
path.insert(0, bottom);
path.smooth();
}
function onMouseUp(event) {
path.add(event.point);
path.closed = true;
path.smooth();
}
</script>
to a stand alone javascript like:
paper.install(window);
window.onload = function() {
paper.setup('myCanvas');
tool.minDistance = 10;
tool.maxDistance = 45;
var path;
function onMouseDown(event) {
path = new Path();
path.fillColor = {
hue: Math.random() * 360,
saturation: 1,
brightness: 1
};
path.add(event.point);
}
function onMouseDrag(event) {
var step = event.delta / 2;
step.angle += 90;
var top = event.middlePoint + step;
var bottom = event.middlePoint - step;
path.add(top);
path.insert(0, bottom);
path.smooth();
}
function onMouseUp(event) {
path.add(event.point);
path.closed = true;
path.smooth();
}
}
it give me an error:
TypeError: undefined is not an object (evaluating 'tool.minDistance =
10')
What is tool here? I understand that I might need to declare it before I can use it. Any idea how to resolve this?
You need to make the global scope as outlined in the documentation :
paper.install(window);
Then get on with global defs. :
window.onload = function() {
// Get a reference to the canvas object
paper.setup('myCanvas');
// In your case create tools
var tool = new Tool();
tool.minDistance = 10;
tool.maxDistance = 45;
Then continue as usual, this will set up your tools.. More can be found here.
Incidentally you've actually already done this correctly for Path(), so the same applies to Tool()
When I use Paper.js directly in javascript I prefer to create paper object this way:
var canvas = document.getElementById('canvas-line');
paper.setup(canvas);
// and then if you want to create some Paper.js object prefix it's name with paper
var myPath = new paper.Path();
If you want to use tool you need to decelerate it with new paper.Tool();
For example if you want to check whether path was clicked:
var tool1 = new paper.Tool();
var handle;
var myPath;
myPath.fullySelected = true;
tool1.onMouseDown = function(event) {
handle = null;
// Do a hit test on path for handles:
var hitResult = myPath.hitTest(event.point, {
handles: true,
fill: true,
stroke: true,
segments: true,
tolerance: 2
});
if (hitResult) {
if (hitResult.type == 'handle-in') {
handle = hitResult.segment.handleIn;
} else if (hitResult.type == 'segment') {
handle = hitResult.segment.point;
} else if (hitResult.type == 'handle-out') {
handle = hitResult.segment.handleOut;
}
}
}
You can find more informations about tools in here http://paperjs.org/reference/tool/

Cancel collision after initial detection, using phaser in js

When 2 sprites collide I'd like to count that as 1 life lost, and then cancel the collision so the sprites pass over one another. I don't want multiple lives to be lost as the same 2 sprites pass over each other.
Any ideas how I can cancel the contact after I've subsracted 1 life?
http://jsfiddle.net/bobbyrne01/44zmvm8z/
javascript ..
var player,
emitter,
lives = 5;
var game = new Phaser.Game(
800,
600,
Phaser.CANVAS,
'Game', {
preload: preload,
create: create,
update: update,
render: render
});
function preload() {
game.load.image('missile', 'http://images.apple.com/v/iphone-5s/a/images/buystrip_retail_icon.png');
game.load.image('player', 'http://38.media.tumblr.com/avatar_0714f87e9e76_128.png');
}
function create() {
game.physics.startSystem(Phaser.Physics.ARCADE);
game.physics.arcade.gravity.y = 300;
game.stage.backgroundColor = '#000';
game.scale.fullScreenScaleMode = Phaser.ScaleManager.SHOW_ALL; // Maintain aspect ratio
player = game.add.sprite(game.world.width / 2, game.world.height / 2, 'player');
player.scale.setTo(0.5, 0.5);
game.physics.arcade.enable(player);
player.body.allowGravity = false;
emitter = game.add.emitter(0, 100, 100);
emitter.makeParticles('missile');
emitter.gravity = 200;
emitter.width = 500;
emitter.x = game.world.width / 2;
emitter.y = -300;
emitter.minRotation = 0;
emitter.maxRotation = 0;
emitter.setScale(0.1, 0.5, 0.1, 0.5, 6000, Phaser.Easing.Quintic.Out);
emitter.start(false, 2000, 500);
}
function update() {
game.physics.arcade.collide(player, emitter, chec, change, this);
if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) {
player.x -= 4;
} else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) {
player.x += 4;
}
}
function chec() {}
function change() {
lives--;
return false;
}
function render() {
game.debug.text('Lives: ' + lives, 2, 28, "#00ff00");
}
Is it optional to destroy the particle just after the collision? If yes, do it this way, just edit the change() function like this:
function change(a, b) {
b.destroy();
lives--;
return false;
}
The second parameter happens to be the particle itself, the first one is the emitter.

Creating a class of Crafty JS entity (class of a class?)

I am trying to create a class which creates a Crafty entity with specific properties. So far, the functions within the class do not run because 'this' refers to the window object
$(document).ready(function () {
Crafty.init(window.innerWidth, window.innerHeight);
var player = new controller(37,38,39,40);
player.d.color("red").attr({
w: 50,
h: 50,
x: 0,
y: 0
});
// Jump Height = velocity ^ 2 / gravity * 2
// Terminal Velocity = push * (1 / viscosity)
var gravity = 1;
var viscosity = 0.5;
var frame = (1 / 20);
var distanceMultiplier = 10; //pixels per meter
var timeMultiplier = 20; //relative to actual time
var keystart = [];
var keyboard = [];
function controller (controls) {
this.d = Crafty.e();
this.d.addComponent("2D, Canvas, Color, Collision");
this.d.collision();
this.d.mass = 1;
this.d.a = {
extradistance : 0,
velocity : 0,
acceleration : 0,
force : 0,
resistance : 0
};
this.d.a.push = 0;
this.d.v = {
extradistance : 0,
velocity : 0,
acceleration : 0,
force : 0
};
this.d.jumping = true;
this.d.onHit("Collision", function () {
var a = this.d.hit("Collision");
if (a) {
for (var b in a) {
this.d.x = this.d.x - a[b].normal.x * a[b].overlap;
this.d.y = this.d.y - a[b].normal.y * a[b].overlap;
if (a[b].normal.y < -0.5) {
this.d.jumping = false;
}
if (Math.abs(a[b].normal.x) < 0.2) {
this.d.v.velocity = this.d.v.velocity * a[b].normal.y * 0.2;
}
if (Math.abs(a[b].normal.y) < 0.2) {
this.d.a.velocity = this.d.a.velocity * a[b].normal.x * 0.2;
}
}
return;
}
});
this.d.physics = function () {
if (keyboard[arguments[1]] && !this.jumping) {
this.v.velocity = 5;
this.jumping = true;
}
if (keyboard[arguments[1]] && this.jumping) {
var now = new Date();
if (now.getTime() - keystart[arguments[1]].getTime() < 500) {
this.v.velocity = 5;
}
}
if (keyboard[arguments[0]] && keyboard[arguments[2]]) {
this.a.velocity = 0;
} else {
if (keyboard[arguments[0]]) {
this.a.velocity = -3;
}
if (keyboard[arguments[2]]) {
this.a.velocity = 3;
}
}
if (keyboard[arguments[3]]) {
this.v.velocity = -5;
}
this.a.force = this.a.push - this.a.resistance;
this.a.acceleration = this.a.force / this.mass;
this.a.velocity = this.a.velocity + (this.a.acceleration * frame);
this.a.extradistance = (this.a.velocity * frame);
this.a.resistance = this.a.velocity * viscosity;
this.attr({
x: (this.x + (this.a.extradistance * distanceMultiplier))
});
this.v.force = gravity * this.mass;
this.v.acceleration = this.v.force / this.mass;
this.v.velocity = this.v.velocity - (this.v.acceleration * frame);
this.v.extradistance = (this.v.velocity * frame);
this.attr({
y: (this.y - (this.v.extradistance * distanceMultiplier))
});
setTimeout(this.physics, (frame * 1000) / timeMultiplier);
};
this.d.listen = function(){ document.body.addEventListener("keydown", function (code) {
var then = new Date();
if (!keyboard[code.keyCode] && !this.jumping && code.keyCode == arguments[1]) { //only if not yet pressed it will ignore everything until keyup
keyboard[code.keyCode] = true; //start movement
keystart[code.keyCode] = then; //set time
}
if (!keyboard[code.keyCode] && code.keyCode != arguments[1]) { //only if not yet pressed it will ignore everything until keyup
keyboard[code.keyCode] = true; //start movement
keystart[code.keyCode] = then; //set time
}
});
};
}
player.d.physics();
player.d.listen();
document.body.addEventListener("keyup", function (code) {
keyboard[code.keyCode] = false;
});
});
In trying to put the functions as prototypes of the class, I run into a problem.
Crafty.init(500,500);
function block () {
block.d = Crafty.e("2D, Color, Canvas");
block.d.color("red");
block.d.attr({x:0,y:0,h:50,w:50});
}
block.d.prototype.green = function() {
this.color("green");
}
var block1 = new block();
block1.d.color();
If an object is defined in the constructor, I cannot use it to add a prototype to.
Generally in Crafty, we favor composition. That is, you extend an entity by adding more components to it. You can have kind of a hierarchy by having one component automatically add others during init.
I haven't looked through all of your example code, because there's a lot! But consider the second block:
function block () {
block.d = Crafty.e("2D, Color, Canvas");
block.d.color("red");
block.d.attr({x:0,y:0,h:50,w:50});
}
block.d.prototype.green = function() {
this.color("green");
}
var block1 = new block();
block1.d.color();
You're trying to combine Crafty's way of doing things (an entity component system) with classes in a way that's not very idiomatic. Better to do this:
// Define a new component with Crafty.c(), rather than creating a class
Crafty.c("Block", {
// On init, add the correct components and setup the color and dimensions
init: function() {
this.requires("2D, Color, Canvas")
.color("red")
.attr({x:0,y:0,h:50,w:50});
},
// method for changing color
green: function() {
this.color("green");
}
});
// Create an entity with Crafty.e()
block1 = Crafty.e("Block");
// It's not easy being green!
block1.green();

PIXI JS Priority call mouseover event

When we create multiple sprites, the function mouseover is called when any hover in hitArea polygon. Regardless, whether applied to another object.
Visibility of sprite governed by sorting the array. The later was added to the sprite in stage.children, the higher it will be. Here is an example in which one rectangle superimposed on the other. At the same time, when we put things on the upper left corner of the bottom sprite, at the top of the object function mouseover will work call, although it is under the other.
How to solve this problem? hitarea not suitable, since the facilities will be constantly dragging.
Thanks in advance for your reply!
var stage = new PIXI.Stage(0x97c56e, true);
var renderer = PIXI.autoDetectRenderer(window.innerWidth, window.innerHeight, null);
document.body.appendChild(renderer.view);
renderer.view.style.position = "absolute";
renderer.view.style.top = "0px";
renderer.view.style.left = "0px";
requestAnimFrame( animate );
var texture = new PIXI.RenderTexture()
r1 = new PIXI.Graphics()
r1.beginFill(0xFFFF00);
r1.drawRect(0, 0, 400, 400)
r1.endFill()
texture.render(r1);
var texture2 = new PIXI.RenderTexture()
r1 = new PIXI.Graphics()
r1.beginFill(0xDDDD00);
r1.drawRect(0, 0, 300, 300)
r1.endFill()
texture2.render(r1);
createBunny(100, 100, texture)
createBunny(120, 120, texture2)
function createBunny(x, y, texture) {
var bunny = new PIXI.Sprite(texture);
bunny.interactive = true;
bunny.buttonMode = true;
bunny.anchor.x = 0.5;
bunny.anchor.y = 0.5;
bunny.scale.x = bunny.scale.y = 0.5;
bunny.mouseover = function(data) {
console.log('mouse over!')
}
bunny.mousedown = bunny.touchstart = function(data) {
this.data = data;
this.alpha = 0.9;
this.dragging = true;
this.sx = this.data.getLocalPosition(bunny).x * bunny.scale.x;
this.sy = this.data.getLocalPosition(bunny).y * bunny.scale.y;
};
bunny.mouseup = bunny.mouseupoutside = bunny.touchend = bunny.touchendoutside = function(data) {
this.alpha = 1
this.dragging = false;
this.data = null;
};
bunny.mousemove = bunny.touchmove = function(data) {
if(this.dragging) {
var newPosition = this.data.getLocalPosition(this.parent);
this.position.x = newPosition.x - this.sx;
this.position.y = newPosition.y - this.sy;
}
}
bunny.position.x = x;
bunny.position.y = y;
stage.addChild(bunny);
}
function animate() {
requestAnimFrame( animate );
renderer.render(stage);
}
http://jsfiddle.net/sD8Tt/48/
I know this is old thread, but for those who come to this page, the issue can be fixed by extending the PIXI's Sprite Class, Look at the following code.
PIXI.Sprite.prototype.bringToFront = function() {
if (this.parent) {
var parent = this.parent;
parent.removeChild(this);
parent.addChild(this);
}
}
And call above method in mousedown event like this
this.bringToFront();
Working JSFiddle
http://jsfiddle.net/6rk58cqa/

Categories