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);
I'm having an issue with obj.getTransformedBounds() of a Shape().
The getBounds() method should return a rectangle (if it was set using setBounds()) which stores the x/y/w/h of the Shape() within its OWN coordinate space - it's real width & height with no transformations applied.
The getTransformedBounds() method should return a rectangle (again, if set by using setBounds()) which stores the x/y/w/y within its PARENT'S coordinate space - the shape's width & height with transformations applied.
Instead, I see getBounds() returning only the transformed bounds of a shape that has its scaleX and/or scaleY applied:
http://jsfiddle.net/W9Tb8/1/
var shape = new createjs.Shape();
shape.graphics = new createjs.Graphics().beginFill("#f00").drawRect(0,0,100,100);
shape.setBounds(0,0,100,100);
shape.scaleX = shape.scaleY = 0.5;
console.log(shape.getBounds(), shape.getTransformedBounds()); // returns [0, 0, 50, 50], [0, 0, 50, 50]
// shape.getBounds() should return [0, 0, 100, 100]
// shape.getTransformedBounds() should return [0, 0, 50, 50}
Am I missing something?
It looks like what's happen is EaselJS is reusing some rectangle instances to help reduce memory impact. Have a look at the documentation here for more about it:
http://createjs.com/Docs/EaselJS/classes/Shape.html#method_getBounds
If you want to force it to give you the originally set bounds you'll either need to clone or copy them. I've altered your example to show how to output the original bounds using cloning.
http://jsfiddle.net/W9Tb8/2/
console.log(shape.getBounds().clone(), shape.getTransformedBounds());
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.
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)
How can I get an attribute of a rectangle in an event function? For example, I need to get opacity value.
Here is the code:
var j = R.rect(position_x - 40, position_y - 285, 80, 50);
j.attr({'fill': '#654','stroke':'none'});
j.rotate(20 * z, position_x, position_y);
j.mouseover(function (event) {
this.attr({opacity: "0.5"});
});
I believe you can just use the attr function with one argument like so:
j.attr('opacity');
For more information, check out the Raphael docs for attr: http://raphaeljs.com/reference.html#attr.