CoffeeScript Refactoring shared variable - javascript

I'm new to CoffeeScript and trying to understand how to refactor this example. Since both functions share canvas and context is there a way to pull them out side the functions and share them instead of repeating myself? I'm sure it is something obvious I'm missing, but I have not been able to get it to work.
CoffeeScript File
#draw_rectangle = ->
canvas = document.getElementById("main_canvas")
context = canvas.getContext("2d")
context.fillRect(50, 25, 150, 100)
#draw_square = ->
canvas = document.getElementById("main_canvas")
context = canvas.getContext("2d")
context.fillRect(100, 50, 100, 50)
HTML Body:
<body>
<canvas id="main_canvas"></canvas>
<p><a onclick="draw_square()" href="#">Draw Square</a></p>
<p><a onclick="draw_rectangle()" href="#">Draw Rectangle</a></p>
</body>

Probably the most elegant way would be to use a class, or at least an object, that would hold both methods and the canvas and context variables. The object would also keep track of whether it's been initialized already. Here's a first attempt:
painter =
draw_rectangle: ->
#init() unless #initialized
#context.fillRect 50, 25, 150, 100
draw_square: ->
#init() unless #initialized
#context.fillRect 100, 50, 100, 50
init: ->
canvas = document.getElementById "main_canvas"
#context = canvas.getContext "2d"
#initialized = true
Now, if you later decided that you wanted to have multiple canvases, it'd be very easy to change painter = to class Painter and reuse the code.

You can use a custom helper method to draw a rectangle.
canvasRectangle = (id, x, y, w, h) ->
canvas = document.getElementById(id)
context = canvas.getContext("2d")
context.fillRect(x, y, w, h)
#draw_rectangle = ->
canvasRectangle("main_canvas", 50, 25, 150, 100)
#draw_square = ->
canvasRectangle("main_canvas", 100, 50, 100, 50)

Related

Phaser.js Game objects collide as intended, but collision callback isn't

I am making a simple game where a ball object hits with a bullet object. The objects collide properly, but the callback function(addscore()), included in the same collides() function for the objects, only gets called once(most probably during the objects' first creation).
Here is the code snippet for the create() function. The collision parts are at the bottom:
create: function() {
this.cursors = this.game.input.keyboard.createCursorKeys();
//background
this.cloud = game.add.sprite(50, 100, 'cloud');
this.cloud.scale.setTo(.4,.4);
this.cloud1 = game.add.sprite(250, 200, 'cloud');
this.cloud1.scale.setTo(.5,.5);
this.grass = game.add.sprite(0, 470, 'grass');
game.stage.backgroundColor = '#71c5cf';
//Ball and cannon
game.physics.startSystem(Phaser.Physics.P2JS);
game.physics.p2.restitution = .9;
this.ball = game.add.sprite(200, 245, 'ball');
this.cannon = game.add.sprite(200, 490, 'cannon');
this.ball.anchor.setTo(0.4, 0.4);
this.cannon.anchor.setTo(0.5, 0.5);
this.cannon.scale.setTo(.4,.4);
this.ball.scale.setTo(.4,.4);
game.physics.p2.enable(this.ball);
this.ball.body.setCircle(29);
this.game.debug.body(this.ball)
//gravity and bounce, collision
this.game.physics.p2.gravity.y = 1500;
this.ballCollisionGroup = this.game.physics.p2.createCollisionGroup();
this.bulletCollisionGroup = this.game.physics.p2.createCollisionGroup();
this.game.physics.p2.updateBoundsCollisionGroup();
this.ball.body.setCollisionGroup(this.ballCollisionGroup);
this.ball.body.collides([this.bulletCollisionGroup], this.addscore);
this.bullet = game.add.group();
this.bullet.createMultiple(20, 'bullet');
this.bullet.callAll('events.onOutOfBounds.add', 'events.onOutOfBounds', this.resetbullet);
this.bullet.callAll('anchor.setTo', 'anchor', 0.1, 0.1);
this.bullet.callAll('scale.setTo', 'scale', .1, .1);
this.bullet.setAll('checkWorldBounds', true);
this.bullet.enableBody = true;
this.bullet.physicsBodyType = Phaser.Physics.P2JS;
game.physics.p2.enable(this.bullet);
this.bullet.forEach(function(child){
child.body.setCircle(7);
child.body.setCollisionGroup(this.bulletCollisionGroup);
child.body.collides([this.ballCollisionGroup]);
child.body.collideWorldBounds=false;
}, this);
},
you can view the game here: http://gabrnavarro.github.io/Volleyball.js .
view the source to see the whole code. Thanks for your help. :)
Looking at code snipped you posted here everything looks good for first glance. However when I looked at your code on gh I see that you don't pass addScore as callback but instead just call it.
Changing main.js line 67 from
this.ball.body.collides([this.bulletCollisionGroup], this.addscore(), this);
to
this.ball.body.collides([this.bulletCollisionGroup], this.addscore, this);
Should do the trick.
I have not had any experience with the P2 physics, so this may be completely wrong. When using arcade physics, the groups are initialised in the create function, but the collision is checked in the update function.
So you would have to do is move:
this.ball.body.collides([this.bulletCollisionGroup], this.addscore, this);
To your update function (Along with any other code that checks for collision).
Again, I have not had any experience with the p2 physics system, so this may be incorrect.
Sorry if this did not help.

How to animate image in a canvas using tweenlite

Been searching for a while now and I haven't seen any article on how to animate (loop continuously from right to left) an imaged drawn in a canvas
var context = document.getElementById('my-canvas').getContext('2d');
var cloud = new Image();
cloud.src = 'images/cloud.png';
cloud.onload = function () {
context.drawImage(cloud, 0, 0)
TweenLite.to({x:0,y:0}, 2, {x: 200, y: 200});
}
What am I getting wrong?
edit:
Like so but using tweenlite : fiddle
Your first argument is wrong, it needs to be the target element/object according to their documentation:
TweenLite.to(target, duration, vars);
where target is an object/element, duration as a number, and vars an object.
Example:
TweenLite.to(canvas, 2, {x: 200, y:200});
Just note that if you intend to use the canvas element as target you could might as well just animate the image directly when loaded.
If you intend to move it across the canvas' bitmap without moving the canvas itself you will need to use a custom object (as I understand their docs) as target holding f.ex. the x and y properties, and use the onUpdate callback mechanism. Check their documentation for all the gory details.
var myObj = {
x: 0, // start position
y: 0,
image: cloud
};
TweenLite.to(myObj, 2, {x: 200, y:200, onUpdate: drawImage});
function drawImage() {
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.drawImage(myObj.image, myObj.x, myObj,y);
}
Just replace what you have inside the onload handler with the code above.
Disclaimer: highly untested..

Way to reference objects in Raphael JS?

I am trying to draw shapes based off of where on the screen existing shapes are, so that I only have to change the coordinates of one shape for all the others to shift appropriately. Is there some way to reference a rectangle's x coordinate when constructing another?
For example, the following code does not work as I thought it would:
var paper = Raphael(0, 0, 1000,600);
var rectangleOne = paper.rect(100, 100, 100, 50);
var rectangleTwo = paper.rect(rectangleOne.x, rectangleOne.y + 40, rectangleOne.width + 50, rectangleOne.height);
I get some error that "rectangleOne.x" is undefined and defaults to zero...Any ideas how to fix this? Thanks all!
var rectangleTwo = paper.rect(rectangleOne.attr('x'), rectangleOne.attr('y') + 40, rectangleOne.attr('width') + 50, rectangleOne.attr('height'));

Trouble in writing a small library in JavaScript

I'd been trying to write a small library in Javascript mainly for Canvas drawImage() method.
The main purpose of the library is to pass array values instead of passing single values such as :
// srcPos=[0,0] , size=[90,90], dstPos=[50,50]
function draw_image(context, image, srcPos, size, dstPos, size) {
context.drawImage(image, srcPos[0], srcPos[1], size[0], size[1], dstPos[0], dstPos[1], size[0], size[1]);
}
but when i called this function jzz like this, I'm getting Uncaught ReferenceError :
var canvas = document.getElementById("display"),
frame = canvas.getContext("2d");
var shipInfo = { center:[45, 45], size:[90, 90], radius: 35, angle:45 },
shipImage = new Image(),
pos = [40, 70];
shipImage.src = "ship.png";
function draw() {
draw_image(frame, shipImage, shipInfo.size, pos, shipInfo.size);
}
window.onload = function() {
draw();
}
And Is it possible to implement a method overriding the default drawImage() like this:
frame.draw_image(shipImage, srcPos, shipInfo.size, dstPos, shipInfo.size);
If you want to add a function to the 2d context, javascript makes this easy thanks to the prototype inheritance : You can inject the Context2D object to add or change its function as you wish.
You might want to look at a few addings i made to the context in a small canvas lib i made here : https://github.com/gamealchemist/CanvasLib
Some will tell that injecting is evil, but unless you're on a huge boat i would just say : If you use some graphic library, respect the semantic of existing functions and everything should be fine. If you don't use libs : do whatever it takes !
So, to answer more specifically to your question, your shorter drawImage would give :
CanvasRenderingContext2D.prototype.draw_image = function ( image,
srcPos, size,
dstPos, size) {
this.drawImage(image, srcPos[0], srcPos[1], size[0], size[1],
dstPos[0], dstPos[1], size[0], size[1]);
};
Then you can use the new function on all your contexts :
var canvas = document.getElementById("display"),
frame = canvas.getContext("2d");
frame.draw_image( ... ) ;
Notice that you could use 'rect' objects, which would be arrays with 4 elements, x, y, w, h, and lead to an even shorter syntax.
Edit : i see in your lib that you want to rotate your rect.
First thing is that you don't want to reset the transform. Just save it then restore it.
I would try something closer to this :
var x = dstPos[0],
y = dstPos[1],
halfWidth = dstSize[0]*0.5, // !! not src use >>1 if you know it's an int.
halfHeight = dstSize[1]*0.5, // !! not src ...
angleInRads = angle * Math.PI / 180;
this.save();
this.translate(x+halfWidth,y+halfHeight);
this.rotate(angleInRads);
this.drawImage(image
, center[0], center[1], srcSize[0], srcSize[1]
, -halfWidth, -halfHeight, dstSize[0],dstSize[1]);
this.restore();
Your small image library would fit well inside a javascript object.
A Demo: http://jsfiddle.net/m1erickson/7pZJw/
A javascript object can hold information about your image:
the image itself
the image size (can be automatically calculated for you)
the image centerpoint (can be automatically calculated for you)
Example:
// create a new object
// fill it with the info about the image
var object={
image:shipImage,
width:shipImage.width,
height:shipImage.height,
centerOffsetX:shipImage.width/2,
centerOffsetY:shipImage.height/2,
radius:35,
angle:45,
};
A javascript object can also hold functions that draws the image (as you've done in your code)
Example:
// when you call object.draw the image will be drawn by this function
// which is added to the object itself
draw:function(context,atX,atY,newWidth,newHeight){
context.drawImage(
this.image,
0,0,this.width,this.height,
atX,atY,newWidth,newHeight);
},
A function to create your small image library inside a javascript object might look like this:
function createImageObject(image,radius,angle){
// create a new object
// fill it with the info about the image
var object={
image:image,
width:image.width,
height:image.height,
centerOffsetX:image.width/2,
centerOffsetY:image.height/2,
radius:radius,
angle:angle,
draw:function(context,atX,atY,newWidth,newHeight){
context.drawImage(
this.image,
0,0,this.width,this.height,
atX,atY,newWidth,newHeight);
},
};
return(object);
}
And you can use your ship object library like this:
// create a new ship object
var shipObject=createImageObject(img,35,45);
// draw the ship image using the ship object
// draw at 20,20 with size 75,75
shipObject.draw(frame,20,20,75,75);
BTW, I see you're using the version of drawImage that will scale/clip the source image.
If you just want to draw the full image at its original size you can do this shortcut:
// draw the image full-sized at x,y
context.drawImage(image,x,y);

Dynamically calling Canvas Functions

Is there any way to call canvas functions using apply() or a similar method, as to dynamically call canvas methods or to be able to pass an array of arguments?
Im looking for this effect
context.fillRect.apply(this,args);
If I understand you correctly:
var op = "fillRect";
var args = [
10, 10, 180, 180
];
ctx[op].apply(ctx, args);
Example: http://jsfiddle.net/eZwYQ/
your apply method should work just fine :
function rectangle(){
ctx.fillRect.apply(ctx,arguments);
}
And of course this can get more "dynamic" :
function doSomethingWithCanvas(context,action,arg1,arg2/*,...argn*/){
context[action].apply(context,Array.prototype.slice.call(arguments,2));
}
And you could use the same function to fill a rectangle, to draw a circle or to draw a simple line :
// simple line
doSomethingWithCanvas(ctx,'lineTo',10, 100);
// circle
doSomethingWithCanvas(ctx,'arc',275, 275, 200, 0, Math.PI*2, true);
// fillRect
doSomethingWithCanvas(ctx,'fillRect,10, 10, 180, 180);
PS : by providing the canvas context as an argument, you can use this function to draw on any canvas.

Categories