I want to write a JavaScript function that moves CSS3 transformed (scaled, skewed, rotated) div elements to an absolute position in the container box. We are developing a canvas like application. I am using getBoundingClientRect() to find an absolute position and bounding-rect of the element. However, when I move this element to specified position it does not work because x and y position has translated.
Move to (0, 0):
// pseudocode I tried!
const matrix = getTransformationMatrix(elm); // get elements transormation matrix.
const pos = matrix.applyToPoint(0, 0); // find related coordiate of zeroth postion by applying element's matrix
tranlatePosition(elm, pos.x, pos.y);
Related
I have a question that is math and also JavaScript related. I'm trying to come up with a function to get me the true coordinate within an element that was clicked. The difficulty here is that the element could be rotated.
Otherwise, we could easily get the coordinate within the element on a click by doing using jQuery:
const ELEMENT_X = event.pageX - object.offset().left;
const ELEMENT_Y = event.pageY - object.offset().top;
Not too sure if these JavaScript objects can help calculate the true coordinate of the element:
DOMPoint
DOMMatrix
We know the coordinate of the click on the viewport, the width and height of the element, its offset left/top and we know the rotation angle (could be negative or positive [-180, 180]). So the function I'm trying to get to return the true coordinate of of any four-sided shape would look something like this:
function getClickedCoordinateOfElement(event, elementDOM, rotationAngleInDegrees) {
let coordinate[];
let x, y;
...
return(coordinate[x, y]);
}
I think the solution requires some manipulation of the Sin or Cos of a triangle, but I'm not too sure.
I forgot to mention that the element is not anchored to the origin and could be anywhere.
How would we get the coordinate of the green dot in relative to the inside of the element? The top left corner (ax) of the element would be (0, 0).
I would like to scale animate an SVG element to fit (preserving aspect ratio) a given area of the SVG.
I know about animate which performs relative animations
var s = Snap("#myelement");
s.animate({ 'transform' : 't100,100s5,5,165,175' },1000);
In principle it should be possible to achieve what I want by computing the parameters of the translation and the scaling.
The problem there is that I do not find accurate documentation of the parameters.
The arguments of t seem to be the relative x,y position and that of s the scale factors and the coordinates of the scale center.
However, how does the combined translation and scaling work? Does the relative translation position scale with the scaling, etc.?
In other words: How do I compute the relative translation and scaling parameters from the coordinates of the upper left and the lower right corner of the animation target element?
Alternatively: Is there a more suitable animate function in Snap?
You show a transform with several parts. The order of these parts is important. If you translate first and scale later, the resulting translation is scaled too. If you scale first and then translate the resulting translation is not affected by the scaling.
The animation you use in Snap.svg is the one I also use. (However I consider migrating to svg.js, since Snap.svg does not play well with Electron for example. I have to do some testing first, though)
Since Snap uses SVG syntax, to solve the problem one needs to understand SVG transformations (see here for an introduction: https://sarasoueidan.com/blog/svg-transformations/). In order to set up a combined SVG transformation it is important to understand that each transformation changes the coordinate system (rather than just the properties of an element in an absolute coordinate frame).
If you combine two transformations, scaling and translation, this means that the parameters of the second transformation depends on the first one.
To achieve a translation and scaling of an element to a given location and size in the coordinates of the ViewBox of an SVG, one can first perform the scaling to the new size choosing the center coordinates for the scaling as the center of the element. Then considerations for the following translations simplify as follows
function startAnimation() {
var svg = Snap("#baseSVG");
/* get the bounding box of the svg */
var bboxSvg = svg.getBBox();
var s = Snap("#element");
/* get the bounding box of the element */
var bbox = s.getBBox();
/* get the required scale factor (assuming that we want to fit the element inside the svg bounding box) */
var scale = Math.min(bboxSvg.width/bbox.width,bboxSvg.height/bbox.height)*0.8;
/* compute the translation needed to bring center of element to center of svg
the scale factor must be taken into account since the translation is based on the coordinate system obtained after the previous scaling */
var tx = (200-bbox.cx)/scale;
var ty = (200-bbox.cy)/scale;
/* perform the animation (make center of scaling the center of element) */
s.animate({ 'transform' : 's' + scale + ',' + scale + ',' + bbox.cx + ',' + bbox.cy + 't' + tx + ',' + ty },1000,mina.bounce);
s.drag();
}
This assumes that your SVG object has id baseSVG and the element you want to transform has id element. It is transformed such that it fits the SVG (adjust the factor 0.8 if you want it larger or smaller). If you know only the coordinates of the corners of the element you must first compute the center coordinates of the target (replace bbox.cx and bbox.cy) and the scale to apply this code snippet. This works in the obvious way in the coordinate frame of baseSVG.
I am new to canvas and I need to know the coordinates where the mouse was clicked. I was successful with:
var canvas = document.getElementById('MainMap');
canvas.addEventListener('mousedown', onMapMouseDown, false);
The problem comes when I try to get the right coordinates. I tried
var x = ev.x - canvas.offsetLeft;
var y = ev.y - canvas.offsetTop;
except that canvas.offsetLeft = 0 and canvas.offsetTop = 40. (Actually it is right since the canvas is placed at that position in a div).
The div is in another div that is in a table that is in a div..... and so on.
The Solution to get the right position would be by adding offsets in a recursive function by getParentNode();
The question is then. How do I now I've reached the Top level node. May be the document or the top level div or table.
You don't need to traverse every node. offsetLeft and offsetTop represent the distance of the element respect to its offset parent. And what is the offsetParent? Well, it's the first ancestor of a node that has a position different from static (this is, relative, absolute or fixed).
Fortunately for you, DOM elements contain a property named offsetParent which indicates what ancestor is the offset parent, so...
var offset = {x: 0, y: 0};
var node = canvas;
while (node) {
offset.x += node.offsetLeft;
offset.y += node.offsetTop;
node = node.offsetParent;
}
This goes on until node is the document, which is placed at (0, 0).
Another, quicker way, is uing jQuery:
var offset = $('#MainMap').offset();
I believe you need to use the 'screen' width and height.. Script libraries like Kinetic.js make this a lot easier for you. Still, if you insist on writing the functionality yourself, I found this tutorial quite helpful:
http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
Or this blog post:
http://miloq.blogspot.be/2011/05/coordinates-mouse-click-canvas.html
I have written a function that will resize an SVG path, or any shape. However when i use it the path does gets resized but unfortunatetly it also changes position within my svg-canvas.
this is my function
function output()
{
var transformw=prompt("Enter your new width");
var transformh=prompt("Enter your new height");
var lastw = svg_1.getBoundingClientRect().width;
var lasth = svg_1.getBoundingClientRect().height;
newW=transformw/lastw;
newH=transformh/lasth;
alert(newH);
alert(newW);
svgCanvas.changeSelectedAttribute("transform",
"matrix(" + newW + ", 0, 0, " + newH + ", 0, 0)");
svgCanvas.recalculateAllSelectedDimensions();
}
I only want the shapes to be positioned on the top corner of my canvas once they get transformed. Ideally i would want them to have the same x,y position they had before the transformation however i wouldnt mind to have a fixed point if the original x,y position is difficult to achieve.
i am answering my own question.
When we resize an SVG element using transform the elements gets moved in the x,y axis relative to the transformation we did.
To counteract this effect we just need to apply a negative translation on the element that has the same ''transformation'' parameters albeit negatively(it moves it to the opposite direction than what the transformation does.
This way we counteract the positioning effects of a tranformation and we only get the resizing effects.
Using Javascript how can I identify the element at a given position? Basically I'm looking to write a function that takes two input parameters (the x and y coordinates) and returns the html element at the position on the screen represented by the parameters.
document.elementFromPoint(x, y)
document.elementsFromPoint(x, y)
https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
https://developer.mozilla.org/en-US/docs/Web/API/Document/elementFromPoint
https://developer.mozilla.org/en-US/docs/Web/API/Document/elementsFromPoint
You can use the native JavaScript elementFromPoint(x, y) method, that returns the element at coordinates x,y in the viewport.
See the elementFromPoint w3c draft
And, a code sample:
function changeColor(newColor) {
// Get the element placed at coords (2, 2)
var elem = document.elementFromPoint(2, 2);
// Set the foreground color to the element
elem.style.color = newColor;
}
<p id="para1">Change this text color using the following buttons.</p>
<button onclick="changeColor('blue');">Blue</button>
<button onclick="changeColor('red');">Red</button>
You can use setInterval() to continuously check the element's hover event but it's not recommended, try to use .hover(...) and css instead to enhance the application performance.
To get the topmost element at a specific position relative to the viewport, document.elementFromPoint(x, y) can be used.
To obtain an array of all the elements at a specific position, use document.elementsFromPoint(x, y).
In both cases, x is the horizontal coordinate which is relative to the left of the viewport and y is the vertical coordinate which is relative to the top of the viewport.