Getting the Screen Pixel coordinates of a Rect element - javascript

I am trying to get the screen pixel coordinates of a rectangle in SVG via java script.
When the rectangle has been clicked, I can figure out its width, height, x and y position with getBBox().
But these positions are the original positions. I want the screen position.
For example if I manipulate the viewBox of the whole SVG, these getBBox coordinates are not any more the same than the screen pixels. Is there a function or way to get the
coordinates considering the current viewBox and the pixel size of svg element?

Demo: http://phrogz.net/SVG/screen_location_for_element.xhtml
var svg = document.querySelector('svg');
var pt = svg.createSVGPoint();
function screenCoordsForRect(rect){
var corners = {};
var matrix = rect.getScreenCTM();
pt.x = rect.x.animVal.value;
pt.y = rect.y.animVal.value;
corners.nw = pt.matrixTransform(matrix);
pt.x += rect.width.animVal.value;
corners.ne = pt.matrixTransform(matrix);
pt.y += rect.height.animVal.value;
corners.se = pt.matrixTransform(matrix);
pt.x -= rect.width.animVal.value;
corners.sw = pt.matrixTransform(matrix);
return corners;
}
The magenta squares are absolutely-positioned divs in the HTML element, using screen space coordinates. When you drag or resize the rectangles this function is called and moves the corner divs over the four corners (lines 116-126). Note that this works even when the rectangles are in arbitrary nested transformation (e.g. the blue rectangle) and the SVG is scaled (resize your browser window).
For fun, drag one of the rectangles off the edge of the SVG canvas and notice the screen-space corners staying over the unseen dots.

You can also check for the existence of the elm.getScreenBBox() method, which does what it sounds like. It's defined in SVG Tiny 1.2.
See the files here, which include a fallback implementation of getScreenBBox that should work in all browsers.

Related

Get pixel color from resized canvas, on mouseover

I have checked this question which provides the perfect answer. But my problem is slightly different. I have a canvas of 300 x 300 and i am re-sizing the canvas using css to 200 x 60. If i re-size the canvas using css i am not able to get the color value onmouseover.
In the re-sized fiddle if you mouse over right below the red or blue rectangles you will notice it still says #FF0000 & #0000FF respectively while it should be #000000. So how to make it work even with re-sized canvas?
Fiddle: Re-sized with css.
Fiddle: Non re-sized.
You need to apply a scale factor inside the mouse handler method. The scale factor is the relationship between your canvas's bitmap (actual size) and the element size (CSS size).
For example:
// find scale:
var sx = example.width / parseInt(example.style.width, 10);
var sy = example.height / parseInt(example.style.height, 10);
// apply to x/y
x = (x * sx)|0; // scale and cut any fraction to get integer value
y = (y * sy)|0;
Updated fiddle
In addition the code need to have some boundary check of the coordinates so getImageData() won't fail (not shown here).

Using svg.js and svg-pan-zoom how can I get current "viewport" center point?

I'm using svg.js and the great svg-pan-zoom plugin ( https://github.com/ariutta/svg-pan-zoom ).
Now I want to have a button that when I click on it it draws a circle on the current center point of the viewport am I seeing (so the position is dynamic depending on the pan/zoom I've done).
I setup a quick example here that draws the circle statically on a specified position.
Please note I have some specific needs: I'm showing a "background" image with a scaling factor like this:
var map = mainWindow.group();
map.addClass('seatMap');
map.image('http://upload.wikimedia.org/wikipedia/commons/b/b3/World-map-2004-cia-factbook-large-1.7m-whitespace-removed.jpg').loaded(function () {
map.cx(viewboxW / 2);
map.cy(viewboxH / 2);
}).scale(0.70);
So how can I get the current center point?
Find the initial centerX and centerY positions(Before zoom) using the code below.
var viewPort = document.getElementsByClassName("viewport")[0];
var bbox = viewPort.getBBox();
var initX = (bbox.x+bbox.width)/2,
initY = (bbox.y+bbox.height)/2;
You can calculate the center point, after zoom using the code written below.
var pt = draw.node.createSVGPoint();
pt.x = initX;
pt.y = initY;
pt = pt.matrixTransform(viewPort.getCTM());
pt.x will be the centerX and pt.y will be the centerY positions.
Hope this helps.
svg-pan-zoom has 2 API methods that may help you:
getSizes
getPan
You may be mostly interested in getSizes as it will return current size of bounding box, real zoom and viewport sizes and positinos.
I had a similar problem: I wanted to add a rect on the top-left corner of the actual zoomed canvas (the zoomable svg), so I made it this way:
var positionX = -1*svgPanZoom.getPan.x/svgPanZoom.getSizes.realZoom;
var positionY = -1*svgPanZoom.getPan.y/svgPanZoom.getSizes.realZoom;
Hope it helps

How to reliably get the exact location of a click event on a canvas?

How do I get the exact point that was clicked on a canvas?
JS-Fiddle: http://jsfiddle.net/IQAndreas/yRMZ6/
None of the "built in" event properties (clientX, pageX, screenX) get the value I am searching for.
Searching Google or previous question reveals several answers, but most of them assume things like "the canvas must be fullscreen", or for some other reason, they fail to work in the test above.
The main problem in your example is padding and real position of canvas start...
You can obtain padding using getComputedStyle function, and find real canvas start on page with this function:
function getPos(el) {
var pl = parseInt(window.getComputedStyle(el, null).getPropertyValue('padding-left'));
var pt = parseInt(window.getComputedStyle(el, null).getPropertyValue('padding-top'));
for (var lx=0, ly=0;
el != null;
lx += el.offsetLeft, ly += el.offsetTop, el = el.offsetParent);
return {x: lx + pl,y: ly + pt};
}
Workin' example on JSFiddle
Please note that border affects that aswell, so you may want to add window.getComputedStyle(el, null).getPropertyValue('border-top') and window.getComputedStyle(el, null).getPropertyValue('border-left') aswell.
You can use .getBoundingClientRect to get the canvas element position--including scrolling:
var BB=canvas.getBoundingClientRect();
Then you can get the mouse position relative to the canvas using event.clientX and event.clientY adjusted by the bounding box of the canvas element.
Note about borders and padding: You can click on the border of a canvas element and it will fire a canvas click event. This means [0,0] on the canvas is on the top-left corner of the canvas border. However the drawing area of a canvas is inside the border. Therefore, if you want to get the x,y of a click relative to the canvas drawing area you must also subtract the border size. The same is true about padding. Therefore if you want to get the x,y of a click relative to the canvas drawing area you must also subtract the padding size.
function handleMousedown(e){
var BB=canvas.getBoundingClientRect();
var x=e.clientX-BB.left-borderLeftWidth-paddingLeft;
var y=e.clientY-BB.top-borderTopWidth-paddingTop;
console.log(x,y);
}
To get the border and padding you can use .getComputedStyle:
// a function using getComputedStyle to fetch resolved CSS values
function getStyle(elem,stylename){
return(
window
.getComputedStyle(elem,null)
.getPropertyValue(stylename)
);
}
// Fetch border and padding
var borderLeftWidth=getStyle(canvas,"border-left-width");
var borderTopWidth=getStyle(canvas,"border-top-width");
var paddingLeft=getStyle(canvas,"padding-left");
var paddingTop=getStyle(canvas,"padding-top");

Canvas - transforming mouse coordinates on window resize

I'm working on a canvas project which includes some drag and drop functionality for some bitmap images on the canvas. I'm having trouble selecting images after the canvas has been resized.
At the minute what i've got is working fine for when the canvas is at it's default dimensions.
I'm drawing all my graphics to a canvas then drawing that image to another canvas and displaying that.
So I want to be able to 'select' an image on the canvas.
Obviously I do this by comparing the mouse coordinates with the coordinates of the object.
mouseX = mouse.x - window.offsetLeft; //I'm accounting for the offset
I'm then multiplying the mouse coordinates by (theVisibleCanvas.width / originalCanvas.width) to account for the scaling of the canvas.
This all works fine for when the window is maximized ( the default dimensions) but once I've resized the window the coordinates start getting further and further out.
Can anyone point out any steps involved in translating mouse coordinates in relation to the canvas resizing?
The line of you provided has little to do with canvas as it uses the offset of the window rather than the offset of the canvas.
To get the coordinates from the mouse adjusted to canvas you can use something like this:
function getXY(canvas, event){
var rect = canvas.getBoundingClientRect(), /// get absolute rect. of canvas
x = event.clientX - rect.left, /// adjust for x
y = event.clientY - rect.top; /// adjust for y
return {x:x, y:y}; /// return object
}
Now you can call this:
var pos = getXY(myCanvasElement, theEvent);
console.log(pos.x, pos.y); /// use the pos object for something
Of course, myCanvasElement is a reference to your actual canvas element and theEvent provided by the callback.

Resizing images (in SVG format) after applying "rotate" transformation

I've been working on an application that allows users to add images to a viewport, then resize them, crop them and rotate them. For the rotation to work, the images are added as SVG's, using the Raphael.js library.
All images have an outline (called wrapper). When an image is resized, so is its wrapper. When the image is rotated, the wrapper expands too, so that it will contain the rotated image (see image bellow).
So far, so good. Except that when I attempt to resize an image object AFTER it has been rotated, it won't be contained within its outline anymore (see image bellow).
When resizing an image, the svg oject's "x" and "y" attributes also need to be adjusted. Both the new size (width + height) of the image and the "x" and "y" attributes are computed as seen bellow:
var ratio_width = w / init.wrapper.width, // w = the new width of the wrapper (computed at each resize step)
ratio_height = h / init.wrapper.height, // h = the new height of the wrapper
svg_width = init.svg.width * ratio_width, // svg_width = the new width of the image
svg_height = init.svg.height * ratio_height, // svg_height = the new height of the image
x = init.svg.x*ratio_width,
y = init.svg.y*ratio_height;
this.svg.attr({
"x": x,
"y": y,
"width": svg_width,
"height": svg_height
});
The "init" JavaScript object contains the size (width & height) of the wrapper AND the size of the image (after the image is rotated, their dimensions will differ), but also the values of the x and y attributes BEFORE the resize operation.
The thing is that all the values computed above are correct. I know because I've also tried destroying the svg at each resize step and re-appending it (but it's not an option, there's too much of a flicker), with those same values for size, x and y, like so:
// "this.angle" on the second line is the current rotation angle
this.svg.remove();
this.svg = this.canvas.image(this.src,x,y,svg_width,svg_height).transform("r"+this.angle);
So if I simply remove the object and then create it again with the values above, why won't simply changing its attributes do the job as it should? I've been pulling my hair out for 3 days now, so I'd appreciate some help!

Categories