I have a fabricjs canvas with multiple SVG objects which may or may not overlap.
Some of the SVG objects have transparent areas, such that the transparent area of Object A may be positioned on top of a filled area of Object B, as in the diagram below:
In the above diagram, the black border illustrates the SVG Object's bounding box.
Points X & Y illustrate cursor locations during object selection.
I am interested in retrieving the RGBA value at the cursor position, such that the selected object is taken into account.
If the user clicks at either Point X or Point Y, then the selected object (with normal behaviour of fabricjs) is Object A, as Object A is the topmost object and the user has clicked within the bounding box.
What I want to do is retrieve the RGBA value (or just the alpha value will do) of Object A at the cursor position i.e. at both Point X and Point Y, Object A is transparent.
From what I can gather from the fabricjs documentation - I can only see a way to get the RGBA value at a cursor position from the canvas as a whole - not for specific objects. This means that Point X is a transparent value, whilst Point Y is blue with full opacity.
var pixelData = this.canvas.getContext().getImageData(pointer.x, pointer.y, 1, 1).data;
What I'm looking for is a way to query the pixel data for a single object at a specific cursor location, perhaps something like:
var pixelData = selectedObject.getImageData(pointer.x, pointer.y, 1, 1).data;
I understand that fabricjs may not directly support such a feature, but I'm wondering whether anybody has a nice way of achieving this so that I can accurately determine when the user has clicked on a transparent area of an object.
I'm supposing that as I'm using SVG images, the solution may have something to do with figuring out the cursor position in relation to the SVG paths and determining whether the user has clicked on a filled section of the SVG, but I'm just a bit stuck with working out the best way to tackle the problem so I'm really open to any suggestions!
If anybody has any pointers it'd be much appreciated!
Related
After searching the web for just over an hour, I have not found any luck.
I am wondering if it is possible, if so how do I create a transparent image on a js canvas not to be considered as a rectangle, rather only the visible area of it.
For example if you click in a transparent spot on the png the script does not considered that part of the object.
Thank you :)
Yes, you can get info about every pixel on the canvas using context.getImageData
A Demo: http://jsfiddle.net/m1erickson/tMmzc/
This code will get an array containing info about every pixel on the canvas:
var data=ctx.getImageData(0,0,canvas.width,canvas.height).data;
The data array is organized with 4 sequential element representing the red,green,blue & alpha(opacity) information about one pixel.
The data array's elements #0-3 have the top-left pixel's r,g,b,a info.
The data array's elements #4-7 have the next rightward pixel's r,g,b,a info.
...and so on...
Therefore, given the mouse position on the canvas you can fetch that pixel's alpha info. If the alpha value is zero then that pixel is transparent.
This code will read the alpha value under the mouse and determine if it's transparent:
var isTransparent = data[(mouseY*canvas.width+mouseX)*4+3]>0;
I want to know whether a point is in the "black" area of some image like the one below.
For the time being I created a large array like this (generated outside JavaScript):
area = [ [0,200], [0,201], [0,202], ..., [1,199], [1,200], ...]
to indicate which coordinates are black. Since this is getting very memory heavy for larger areas (I'm talking of image sizes of about 2000x2000 pixels), which kind of algorithm would you choose that is fast and not too memory hungry for finding out whether a specific coordinate is inside the black area?
You can draw the image to a canvas with same width and height as the image and then retrieve the pixelColor from the canvas at the specific point(x|y).
Here is a thread on how to retrieve the pixcel color:
Get pixel color from an image
This is how i retrieve the pixel color from the mouseposition and return a colorcode('#rrggbb'):
var pixelData = canvas.getContext('2d').getImageData(event.offsetX, event.offsetY, 1, 1).data;
var hex= '#' + valToHex(pixelData[0]) + valToHex(pixelData[1]) + valToHex(pixelData[2]);
I expected this to be 100% a no-brainer, but as it turns out, I cannot figure out what the x and y attributes of an svg created and manipulated with Raphael.js mean. I assumed they were the coordinates of the top-left corner of the object in relation to the canvas, but now I'm not so sure.
After creating a canvas (var paper = new Raphael(container,width,height)) and adding an image or rectangle to it, for example, if I retrieve the "x" and "y" attributes using the attr method (e.g. object.attr("x")), they're both at 0. However, if I rotate that object and then retrieve the values of x and y again, the values don't reflect the position of the top-left corner of my object in relation to the canvas anymore.
Can someone please explain this to me?
I fear #afaf12's answer complacently goes only half the distance. He's absolutely correct that transformation logic occurs after the fundamental attributes of a given element and doesn't effect them, but it is certainly possible to retrieve the x and y of that element after transformations are applied. You'll want to use the getBBox method, like this:
var bbox = elem.getBBox();
console.log("Transformed coordinates of element are %s,%s", bbox.x, bbox.y );
Please note that there is some trickiness involved -- this returns the bounding box of the element, which is often a superset of the space occupied by the element -- so there's no guarantee that the returned point will be IN the element.
Another alternative occurs if you're using paths -- path.getPointAtLength also works with transformed coordinates, so you can get the x,y offset of the beginning of a path by calling
var coord = elem.getPointAtLength(0);
console.log("Transformed coordinates of path are %s,%s", coord.x, coord.y );
Rotation is a transformation and it does not change x and y of the object.
http://raphaeljs.com/reference.html#Element.transform
I have used the Canvas code provided elsewhere on this site to create a screen where I have several overlapping transparent pngs with the non-transparent parts being irregular shapes. I can get the color under the cursor and that is great. But my shapes are all the same color and I need a way to get the ID of the particular shape as well so I know which one was clicked on. Imagine a map made of overlapping pngs fo reach country and you want to detect which country was clicked on. From what I can tell, id detection only applies to rectangular regions. Any suggestions?
$('#myCanvas').click(function(e){
var position = findPos(this);
var x = e.pageX - position.x;
var y = e.pageY - position.y;
var coordinate = "x=" + x + ", y=" + y;
var canvas = this.getContext('2d');
var p = canvas.getImageData(x, y, 1, 1).data;
var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
alert(hex);
});
This code gets and displays the color (findPos and rgbToHex are separate functions left off for clarity). I need an id! Help!
Even with transparency, the images are all rectangles. You then know which images are at a clicked point by rectangle intersection - check your array of images and their x,y points with width,height for point intersection. You then come up with an array of possibly clicked images. If there's only one in the list, you are done.
The images have an implied Z-order of the reverse order in which you wrote them, meaning, an image is overwritten by the next image written which overlaps it. You can use that to know which order to try them in for hit-testing if more than one is at the point clicked. The only trick is to detect if an image pixel is transparent or not.
To detect transparency for a pixel point clicked in a single image, you could keep a second hidden canvas element. Clear it, then write the target image to it at the same position, and use the same code to see if the clicked pixel within the second canvas is the transparent color. If it is, repeat this process with the next image in the Z-order until you get the image where a non-transparent pixel was clicked.
A small but important optimization is to check the color clicked first, and if it's the transparent color you already know none of the images were clicked on a non-transparent point.
Is there any trick to determine if user clicks on given element rendered in canvas? For example I'm displaying rhombus from .png file with transparent background and i want to know if user click inside or outside that figure (like mouse-element collision).
There is no concept of individual elements in a canvas - it is simply just an area that you're drawing pixels onto. SVG on the other hand is made up of elements which you can then bind events to. However there are a few approaches you can take to add click events to canvas:
Position an html element that overlays the area on the canvas you want to be clickable. A for a rectangular area or an image map for something more irregular.
Use separate canvases for each element that you want to be clickable.
CAKE - I haven't used this myself, but it's description is "SVG sans the XML". This may cover your needs. Demos here http://glimr.rubyforge.org/cake/canvas.html#EditableCurve
One idea is to draw the image to a temporary canvas, then use getImageDate() to receive data for the pixel you are interested in, and check if its alpha value is 0 ( = transparent).
The following is a sketch of a solution. It is assumed that...
x and y are the coordinates of the mouse click event
you are looping over gameObjects, the current object being stored in the variable gameObject
the game object has been initialized with an image, x and y coordinates
The following code would then check whether the click was on a transparent area:
var tempCanvas = document.createElement('canvas');
if (tempCanvas.getContext) {
tempContext = tempCanvas.getContext('2d');
}
tempContext.drawImage(gameObject.img, 0, 0);
var imgd = tempContext.getImageData(x - gameObject.x, y - gameObject.y, 1, 1);
var pix = imgd.data;
if (pix[3] == 0) {
// user clicked on transparent part of the image!
}
Note: This is probably quite inefficient. I'm sure someone can come up with a better solution.
I have solve this problem using kintech.js, tutorials and examples can be found: http://www.html5canvastutorials.com/kineticjs/html5-canvas-drag-and-drop-tutorial/