Get absolute screen bounds of a DOM element from a Firefox extension - javascript

I have a firefox extension that needs to get the exact screen co-ordinates of a DOM element and passes it to a native DLL via js/c-types.
Now I have it mostly covered :
var gDomWindowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(nsIDOMWindowUtils);
function getScreenRect(oElem)
{
var rc =
{
x : 0,
y : 0,
w : 0,
h : 0
};
var o = oElement;
while(o != null)
{
rc.y += o.offsetTop;
rc.x += o.offsetLeft;
o = o.offsetParent;
}
var x = {}, y = {};
gDomWindowUtils.getScrollXY(false, x, y);
rc.x -= x.value;
rc.y -= y.value;
var scale = gDomWindowUtils.screenPixelsPerCSSPixel;
rc.x *= scale;
rc.y *= scale;
rc.w *= scale;
rc.h *= scale;
return rc;
};
This handles scrolling and zooming, but the values I get are relative to the browser window, and not the screen.
How do I detect the offset of the client area of the actual rendering area of the browser?
I can even use native code (Win32) via js/ctypes so I tried to see if I could use FindWindow() / GetWindowRect() to get it, but the whole of firefox is a single HWND, the controls are all not native windows.
So one idea I have is, since the UI of firefox is an XUL document, I should be able to get the menubar, tab bar etc etc and find the browser areas absolute offset. But, I have no clue how to access the XUL tree that defines the browser UI.
Can someone give me a pointer?
[Edit]
Ignore rc.w and rc.h being undefined in the above code , it is irrelevant to the question.

You mostly got it already but I would recommend using getBoundingClientRect() instead of offsetLeft/offsetTop:
var rect = oElement.getBoundingClientRect();
var rc = {
x: rect.left,
y: rect.top,
w: rect.width,
h: rect.height
};
getBoundingClientRect() considers scrolling so that you no longer need to add it. You get the coordinates relative to screen using window.mozInnerScreenX and window.mozInnerScreenY:
rc.x += window.mozInnerScreenX;
rc.y += window.mozInnerScreenY;
And after that you multiply the values with screenPixelsPerCSSPixel. That should give you proper screen coordinates.

I will add one more "trick" that helped me with a similar problem.
Whenever my code receives a mousemove event, I capture the mouse-cursor positions in ALL the coordinates that event provides, which includes at least "screen", "client" and "pageXY" coordinates. Then for any OTHER purpose I can compute the difference between these coordinates simply by subtracting the appropriate two of those.
In your case, you'd probably compute an offset like this:
deltaX = event.screenX - event.clientX;
deltaY = event.screenY - event.clientY;
or
offsetX = event.screenX - event.pageX;
offsetY = event.screenY - event.pageY;
Then just add deltaX and deltaY or offsetX and offsetY to those coordinates to get screen coordinates.

Related

getboundingclientrect not return same value

Im tracking clicks on my website. For each clicks im taking the coordinates of the click on the clicked element ( referenced with the function getPath who return the css path) with the following function:
add_click(event) {
var element = event.target;
var dimensions = element.getBoundingClientRect();
var click = {
width: window.innerWidth,
height: window.innerHeight,
path: getPath(element),
x: event.offsetX / dimensions.width,
y: event.offsetY / dimensions.height,
key: event.which
};
this.clicks.push(click);
}
Then i'm creating visualisation of clicks. For this im reconverting X and Y coordinates of the element into X and Y coordinates of the window. For this im using :
var element = document.querySelector(path); //return the clicked element
var rect = element.getBoundingClientRect();
var X = rect.left + click["x"] * rect.width;
var Y = rect.top + click["y"] * rect.height;
However getBoundingClientRect() is not returning the same value for an element every time i load page. Value returned from getBoundingClientRect() for a same element keep changing and this creating wrong coordinates for my click.
I have tested it on Chrome 87 and Firefox 78 both are returning different value for getBoundingClientRect() for the same exact window size.
Does anyone know why this is happening, have met this bug before or have another function that is not broken to get coordinates of a DOM element?

HTML Canvas Touch events with Scrollbar

Hi,
I am making a touch enabled signing app using 2 HTML Canvas. In the screenshot attached below, i am able to sign on my tablet on 1st canvas and on the second canvas as well without scrolling the scroll-bar on the right. If i scroll and bring the 2nd canvas in complete focus, then my signing stops working on the 2nd Canvas. It seems that on scrolling i am not calculating the coordinates well.
Here is the code that i am using to get the co ordinates and pass that to my Drawer method.
var coors = {
x: event.targetTouches[0].pageX ,
y: event.targetTouches[0].pageY
};
// Now we need to get the offset of the canvas location
var obj = signCanvas;
if (obj.offsetParent) {
do {
coors.x -= obj.offsetLeft;
coors.y -= obj.offsetTop;
}
while ((obj = obj.offsetParent) != null);
}
Try the following instead:
var rect = signCanvas.getBoundingClientRect(); // absolute position of canvas
var coors = {
x: event.targetTouches[0].clientX - rect.left,
y: event.targetTouches[0].clientY - rect.top
};
and of course, comment out/remove the rest of the adjustment code for the coordinates.

Placing mouse pointer within a given diameter

I want to display an alert when the user moves his mouse pointer from coordinate X=42, Y= 10 to the coordinates X=40, Y=200.
However, since these mouse points are very small, the user might not start and end at the exact coordinates. So what i want to do is to give a range where the user could start and end when the mouse is within a certain diameter.
How can i do this ?
Is it what you're looking for?
$("body").mousemove(function(event) {
var radius = 10, yourX = 40, yourY = 10;
var xDimenion = yourX > event.pageX ? yourX - event.pageX : event.pageX - yourX;
var yDimenion = yourY > event.pageY ? yourY - event.pageY : event.pageY - yourY;
if(Math.sqrt(xDimenion * xDimenion + yDimenion * yDimenion ) < radius){
//do some stuff
}
});
I think it's ok now.
Would Client-Side Image Maps work http://www.tutorialspoint.com/html/html_image_links.htm (bottom) using an Image of a Dot and and "area shape" that is set to "circle"; that gives an exact radius (and the option to use poly) for a mouseover.

Transforming to bound box with Raphael.js

I am in need of some math help. I am trying to dynamically transform my Raphael set of elements to a given bound box within my canvas.
For example, say my canvas (paper) is 600 x 300 and is filled with paths. These paths are all in a set.
Now I want fill my canvas with a given bound box. The bound box is in pixel coordinates. e.g. [[50,10], [100,20]]
So the end result would be a function call that would zoom and position the SVG elements. This would cause the canvas to be cropped to the coordinate bounds.
var bbox = [[50,10], [100,20]]
animateToBoundBox(set, bbox, duration);
function animateToBoundBox(set, bbox, duration) { /* beautiful code */ }
I think the way to accomplish this would be by using the element matrix but I'm not sure. What do you think the most elegant way of handling this would be?
Thanks
The other answers are correct -- you want to use setViewBox.
Here's a version that supports animation. It's not entirely beautiful and you'll have to look at the page source to extract the code, but it should do more or less exactly what you want.
Here's the view box animation as a Raphael extension:
Raphael.fn.animateViewBox = function animateViewBox( x, y, w, h, duration, easing_function, callback )
{
var cx = this._viewBox ? this._viewBox[0] : 0,
dx = x - cx,
cy = this._viewBox ? this._viewBox[1] : 0,
dy = y - cy,
cw = this._viewBox ? this._viewBox[2] : this.width,
dw = w - cw,
ch = this._viewBox ? this._viewBox[3] : this.height,
dh = h - ch,
self = this;;
easing_function = easing_function || "linear";
var interval = 25;
var steps = duration / interval;
var current_step = 0;
var easing_formula = Raphael.easing_formulas[easing_function];
var intervalID = setInterval( function()
{
var ratio = current_step / steps;
self.setViewBox( cx + dx * easing_formula( ratio ),
cy + dy * easing_formula( ratio ),
cw + dw * easing_formula( ratio ),
ch + dh * easing_formula( ratio ), false );
if ( current_step++ >= steps )
{
clearInterval( intervalID );
callback && callback();
}
}, interval );
}
And the (not so beautiful) demonstration is here: http://voidblossom.com/tests/easedViewBox.php
If you're really bent on using transform (which could have a few benefits if leveraged well, but will in general be fragile compared to viewbox manipulation), there's another example using transform located at http://voidblossom.com/tests/zoomByTransform.php.
Not sure if I completely understand what you are looking for, but it sounds like you want to zoom the view to a specific bounding box. Have you looked at the setViewBox function? Basically your function would call it like this:
setViewBox(bbox[0][0], bbox[0][1], bbox[1][0] - bbox[0][1], bbox[1][1] - bbox[0][0])
From what I can tell, everything you want to accomplish would be better handled with Paper.setViewBox() in an animation. See http://raphaeljs.com/reference.html#Paper.setViewBox

Getting mouse location in canvas [duplicate]

This question already has answers here:
How do I get the coordinates of a mouse click on a canvas element? [duplicate]
(22 answers)
Closed 3 years ago.
Is there a way to get the location mouse inside a <canvas> tag? I want the location relative to to the upper right corner of the <canvas>, not the entire page.
The accepted answer will not work every time. If you don't use relative position the attributes offsetX and offsetY can be misleading.
You should use the function: canvas.getBoundingClientRect() from the canvas API.
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
canvas.addEventListener('mousemove', function(evt) {
var mousePos = getMousePos(canvas, evt);
console.log('Mouse position: ' + mousePos.x + ',' + mousePos.y);
}, false);
Easiest way is probably to add a onmousemove event listener to the canvas element, and then you can get the coordinates relative to the canvas from the event itself.
This is trivial to accomplish if you only need to support specific browsers, but there are differences between f.ex. Opera and Firefox.
Something like this should work for those two:
function mouseMove(e)
{
var mouseX, mouseY;
if(e.offsetX) {
mouseX = e.offsetX;
mouseY = e.offsetY;
}
else if(e.layerX) {
mouseX = e.layerX;
mouseY = e.layerY;
}
/* do something with mouseX/mouseY */
}
Also note that you'll need CSS:
position: relative;
set to your canvas tag, in order to get the relative mouse position inside the canvas.
And the offset changes if there's a border
I'll share the most bulletproof mouse code that I have created thus far. It works on all browsers will all manner of padding, margin, border, and add-ons (like the stumbleupon top bar)
// Creates an object with x and y defined,
// set to the mouse position relative to the state's canvas
// If you wanna be super-correct this can be tricky,
// we have to worry about padding and borders
// takes an event and a reference to the canvas
function getMouse = function(e, canvas) {
var element = canvas, offsetX = 0, offsetY = 0, mx, my;
// Compute the total offset. It's possible to cache this if you want
if (element.offsetParent !== undefined) {
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
} while ((element = element.offsetParent));
}
// Add padding and border style widths to offset
// Also add the <html> offsets in case there's a position:fixed bar (like the stumbleupon bar)
// This part is not strictly necessary, it depends on your styling
offsetX += stylePaddingLeft + styleBorderLeft + htmlLeft;
offsetY += stylePaddingTop + styleBorderTop + htmlTop;
mx = e.pageX - offsetX;
my = e.pageY - offsetY;
// We return a simple javascript object with x and y defined
return {x: mx, y: my};
}
You'll notice that I use some (optional) variables that are undefined in the function. They are:
stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingLeft'], 10) || 0;
stylePaddingTop = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingTop'], 10) || 0;
styleBorderLeft = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderLeftWidth'], 10) || 0;
styleBorderTop = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderTopWidth'], 10) || 0;
// Some pages have fixed-position bars (like the stumbleupon bar) at the top or left of the page
// They will mess up mouse coordinates and this fixes that
var html = document.body.parentNode;
htmlTop = html.offsetTop;
htmlLeft = html.offsetLeft;
I'd recommend only computing those once, which is why they are not in the getMouse function.
For mouse position, I usually use jQuery since it normalizes some of the event attributes.
function getPosition(e) {
//this section is from http://www.quirksmode.org/js/events_properties.html
var targ;
if (!e)
e = window.event;
if (e.target)
targ = e.target;
else if (e.srcElement)
targ = e.srcElement;
if (targ.nodeType == 3) // defeat Safari bug
targ = targ.parentNode;
// jQuery normalizes the pageX and pageY
// pageX,Y are the mouse positions relative to the document
// offset() returns the position of the element relative to the document
var x = e.pageX - $(targ).offset().left;
var y = e.pageY - $(targ).offset().top;
return {"x": x, "y": y};
};
// now just make sure you use this with jQuery
// obviously you can use other events other than click
$(elm).click(function(event) {
// jQuery would normalize the event
position = getPosition(event);
//now you can use the x and y positions
alert("X: " + position.x + " Y: " + position.y);
});
This works for me in all the browsers.
EDIT:
I copied the code from one of my classes I was using, so the jQuery call to this.canvas was wrong. The updated function figures out which DOM element (targ) caused the event and then uses that element's offset to figure out the correct position.
GEE is an endlessly helpful library for smoothing out troubles with canvas, including mouse location.
Simple approach using mouse event and canvas properties:
JSFiddle demo of functionality http://jsfiddle.net/Dwqy7/5/
(Note: borders are not accounted for, resulting in off-by-one):
Add a mouse event to your canvas
canvas.addEventListener("mousemove", mouseMoved);
Adjust event.clientX and event.clientY based on:
canvas.offsetLeft
window.pageXOffset
window.pageYOffset
canvas.offsetTop
Thus:
canvasMouseX = event.clientX - (canvas.offsetLeft - window.pageXOffset);
canvasMouseY = event.clientY - (canvas.offsetTop - window.pageYOffset);
The original question asked for coordinates from the upper right (second function).
These functions will need to be within a scope where they can access the canvas element.
0,0 at upper left:
function mouseMoved(event){
var canvasMouseX = event.clientX - (canvas.offsetLeft - window.pageXOffset);
var canvasMouseY = event.clientY - (canvas.offsetTop - window.pageYOffset);
}
0,0 at upper right:
function mouseMoved(event){
var canvasMouseX = canvas.width - (event.clientX - canvas.offsetLeft)- window.pageXOffset;
var canvasMouseY = event.clientY - (canvas.offsetTop - window.pageYOffset);
}
I'd use jQuery.
$(document).ready(function() {
$("#canvas_id").bind( "mousedown", function(e){ canvasClick(e); } );
}
function canvasClick( e ){
var x = e.offsetX;
var y = e.offsetY;
}
This way your canvas can be anywhere on your page, relative or absolute.
Subtract the X and Y offsets of the canvas DOM element from the mouse position to get the local position inside the canvas.

Categories