I'm using the vue-draggable-resizable component that will give the x,y offset coordinates as to where my element was dropped on the page (that all works great). However, I would like to know if there's a way to the determine if the drop coordinates overlaps another element. I basically have pages stacked down the page and would like to know which page the element was dropped over so I can update the page number that the element belongs to.
So, my question is, how can I determine if a given x,y coordinates is overlapping another element?
You could use those drop coordinates with document.elementFromPoint(x,y). The key is to disable pointer-events on <vue-draggable-resizable>'s dragging element so that document.elementFromPoint(x,y) can grab the element underneath.
// template
<vue-draggable-resizable #dragstop="onDragStop">
// script
methods: {
onDragStop(x, y) {
/* For example's sake, this element lookup is simplified in that
only considers the top-left corner given by `(x,y)`, but
you might want to evalute additional coordinates e.g., to meet
a minimum threshold before overlap is verified. */
const el = document.elementFromPoint(x, y);
console.log(el);
}
}
// style
.dragging {
pointer-events: none; /* ignore for document.elementFromPoint() */
}
demo
Related
I'm using ng-drag-drop and highcharts/highstocks.
I need to drag and drop an external item into the highcharts/highstock and create a marker. Per the docs in highcharts/highstocks, you can drag and drop but it is within the chart container.
So what I did was get the event of the entire page.
On drag and drop, I retrieve where I dropped the position relative to the entire web page. My question is, how do I translate the entire page position (pageX, pageY) into highcharts/highstock coordinates (within the plot). The dragged item is dragged from a parent component and into a child component, where the chart is.
//parent component
OnItemDropped(e:any){
//X and Y coordinates of the entire webpage when item is dropped
const add = { coord: { x: e.nativeEvent.pageX, y: e.nativeEvent.pageY }, name:'marker1' }
this.pinnedItems = [...this.pinnedItems, add]
}
so coord.x and coord.y is the entire page. but I need it to set at the page coordinates. Obviously, the position doesn't match with chart's. Is there a way where highcharts can recognize the page's XY coordinates as the chart's XY coordinates?
Alternatively, I thought about retrieving the html value of the page coord. But I cant seem to find out where that resides in the nativeElement object.
I want to make a custom layout which behaves like this shown in picture below.
I have already tried Tile Layout and to modify its calculateDropIndex method but not get desired behavior.
Tile layout works well when all the tiles are of same height and width but in my case tile size are different.
http://gridster.net/
Gridster layout screenshot
After a little bit more try i am able make a gridster like drag n drop gird component.
here is a brief summery of how i have made this component.
while adding pods in to container add one more pod at the same position and keep it's visibility off. this extra node will be our drop indicator.
keep all the original nodes in one array say nodes, and all extra nodes in another array say dropIndicators.
for example if add nodes A, B and C in the container. we will add three extra nodes X, Y and Z respectively one for each main node.
and over nodes array will be
node[0] = A;
node[1] = B;
node[2] = C;
and dropIndicators array will be
dropIndicators[0] = X;
dropIndicators[1] = Y;
dropIndicators[2] = Z;
now you will need following function to update nodes position when we drag any node.
CalculateDropLocation - to calculate new drop location while dragging.
CheckCollision - to check collision between two nodes.
FixCollision - if there is a collision we will fix the collision using this function. this function will mode all the colliding nodes downwards recursively.
updateNodes - once the collision is fixed we will update all the nodes using this function. this function will move node upward if there is an empty space.
now when we start dragging A node calculate new drop location for A node and move it's relative extra node i.e. X to the new drop location, check if X is colliding with any other extra node. we will use the dropIndicators array in all the functions.
if collistion than call fixCollision() function, than call update node function.
and at the end once all the nodes are updated set the position of all the extra nodes to it's original nodes i.e. set X's position to A's position , Y's position to B and so on. while updating original nodes position you can use Move animation to move nodes smoothly in the container.
hope this will help. using this method you can make this type of layout in any language.
hope this will help.
for more details and sample application with source visit this link
http://usefulflexcomponents.blogspot.in/2015/12/blog-post.html
I want to make a D3 graph, which should be as follows:
When the html page is loaded, there will be a single node at a fixed location. Let us say top left. Let us call it template node and this node is non-movable.
When the user does mouse down on the template node, a new node is created at the same location as the template node and the user should be able to drag the new node to where he wants. The new node should remain exactly where the user moves it to.
At any time user should be able to move a node. Again the node should remain where the user leaves it.
User should be able to draw link between any two nodes. Let us assume that if he drags from one node to another without holding down ctrl key, then a link is drawn and if he drags while holding down the control key, then the node moves.
When a link is drawn between two nodes, then the nodes should not change positions.
When two nodes are linked and one of them is moved by dragging it, then the link should change in size and orientation as needed.
I am using force layout.
I am able to create a template node but it always goes to the center of the container - I think it is because the center of the container is the center of gravity. But not sure how to fix its position to the top left through code.
I can create links and new nodes. But the nodes move and links resize. May be it is because force layout tries to make link lengths equal to the link distance in the force layout. But I do not know how to use a function for link distance? I am even not sure if that will really help.
So what method should I use? Any idea?
For force layout, you can set the 'fixed' property of a node to true in order to prevent it from being affected by the simulation. After that, you should be able to set it's position manually. You might choose to do this in a function call:
function pinNode(node) {
node.fixed = true;
}
function unpinNode(node) {
node.fixed = false;
}
I believe you could get a node to the upper left corner with a call like this: pinNode(node, 0, 0). As long as the node has its fixed property set to true, it should remain unaffected by the sim. You might find this snippet from the documentation helpful; it describes how the fixed property is affected by force.drag:
Bind a behavior to nodes to allow interactive dragging, either using
the mouse or touch. Use this in conjunction with the call operator on
the nodes; for example, say node.call(force.drag) on initialization.
The drag event sets the fixed attribute of nodes on mouseover, such
that as soon as the mouse is over a node, it stops moving. Fixing on
mouseover, rather than on mousedown, makes it easier to catch moving
nodes. When a mousedown event is received, and on each subsequent
mousemove until mouseup, the node center is set to the current mouse
position. In addition, each mousemove triggers a resume of the force
layout, reheating the simulation. If you want dragged nodes to remain
fixed after dragging, set the fixed attribute to true on dragstart, as
in the sticky force layout example.
force.drag
Also see here: force layout nodes
If you want to use a function for link distance, include it when you create the force layout:
var force = d3.layout.force()
.size(width, height)
.linkStrength(0.5) // how much can link distance be overridedn by the simulation
.linkDistance(function() {return /* some evaluation */;});
// ...
// You might need to defer the calculation of linkDistance until later,
// such as in update(), since nodes might not have the properties
// that you need to check until that point:
function update() {
force
.nodes(nodes)
.links(links)
.linkDistance(function(link) {
// The function gets called for each link in the simulation.
// Each link will be connected to two nodes, source and target,
// which may be useful in determining link distance.
if (link.source.someProperty || link.target.somePropery) {
return /* something */;
} else {
return /* something else */;
}
});
}
I have a div with an id="div_Diagram" that contains two other divs, div_CanvasHeader and div_Canvas, as shown. div_Canvas also contans an SVG, which in turn contains several rects. Each rect has is constructed in a way that its has a javascript script attached to its mouseover event with the x & y position values passed into the script.
Do to the nature of svgs, I'm not able to correctly position the tooltip division relative to the rect. I'd like to be able to use the rect coordinates (x&y) to position the tooltip relative to div_CanvasHeader or div_Diagram. Other then x & y, all other dimensions are fixed.
Here is the javascript. (Note that the tooltip is a telerik control. The Show() method does show the tooltip with all of the correct content but at the bottom left of page).
function showSVGTtip(elt, x, y) {
if (elt.hasAttribute("name")) {
var ttipText = elt.getAttribute("ttipText ");
var ttip = $find("<%=myToolTipDiv.ClientID %>");
document.getElementById("ttipLabel").innerHTML = ttipText ;
ttip.show();
// Need to set position of ttip relative to other element such as pnlCanvasHeader
}
}
How do I position the tooltip div relative to one of the other divs? For example, if I have the id of the tooltip div, can I move it on the client relative to div_CanvasHeader? Does the tooltip need to be contained within the other div?
I have an inner div inside an outer div. The inner div is draggable and outer is rotated through 40 degree. This is a test case. In an actual case it could be any angle. There is another div called point which is positioned as shown in the figure. ( I am from a flash background . In Flash if I were to drag the inner div it would follow the mouse even if its contained inside an outer rotated div.) But in HTML the inner div does not follow the mouse as it can be seen from the fiddle. I want the div 'point' to exactly follow the mouse. Is this possible. I tried to work it using trignometry but could not get it to work.
http://jsfiddle.net/bobbyfrancisjoseph/kB4ra/8/
Here is my approach to this problem.
http://jsfiddle.net/2X9sT/21/
I put the point outside the rotated div. That way I'm assured that the drag event will produce a normal behavior (no jumping in weird directions). I use the draggable handler to attach the point to the mouse cursor.
In the drag event, I transform the drag offset to reflect the new values. This is done by rotating the offset around the outer div center in the opposite direction of the rotation angle.
I tested it and it seems to be working in IE9, Firefox, and Chrome.
You can try different values for angle and it should work fine.
I also modified the HTML so it is possible to apply the same logic to multiple divs in the page.
Edit:
I updated the script to account for containment behavior as well as cascading rotations as suggested in the comments.
I'm also expirementing with making the outer div draggable inside another div. Right now it is almost working. I just need to be able to update the center of the dragged div to fix the dragging behavior.
Try Dragging the red div.
http://jsfiddle.net/mohdali/kETcE/39/
I am at work now, so I can't do the job for you, but I can explain the mathematics behind the neatest way of solving your problem (likely not the easiest solution, but unlike some of the other hacks it's a lot more flexible once you get it implemented).
First of all you must realize that the rotation plugin you are using is applying a transformation to your element (transform: rotate(30deg)), which in turn is changed into a matrix by your browser (matrix(0.8660254037844387, 0.49999999999999994, -0.49999999999999994, 0.8660254037844387, 0, 0)).
Secondly it is necessary to understand that by rotating an element the axis of the child elements are rotate absolutely and entirely with it (after looking for a long time there isn't any real trick to bypass this, which makes sense), thus the only way would be to take the child out of the parent as some of the other answers suggest, but I am assuming this isn't an option in your application.
Now, what we thus need to do is cancel out the original matrix of the parent, which is a two step process. First we need to find the matrix using code along the following lines:
var styles = window.getComputedStyle(el, null);
var matrix = styles.getPropertyValue("-webkit-transform") ||
styles.getPropertyValue("-moz-transform") ||
styles.getPropertyValue("-ms-transform") ||
styles.getPropertyValue("-o-transform") ||
styles.getPropertyValue("transform");
Next the matrix will be a string as shown above which you would need to parse to an array with which you can work (there are jquery plugins to do that). Once you have done that you will need to take the inverse of the matrix (which boils down to rotate(-30deg) in your example) which can be done using for example this library (or your math book :P).
Lastly you would need to do the inverse matrix times (use the matrix library I mentioned previously) a translation matrix (use this tool to figure out how those look (translations are movements along the x and y axis, a bit like left and top on a relatively positioned element, but hardware accelerated and part of the matrix transform css property)) which will give you a new matrix which you can apply to your child element giving you the a translation on the same axis as your parent element.
Now, you could greatly simplify this by doing this with left, top and manual trigonometry1 for specifically rotations only (bypassing the entire need for inverse matrices or even matrices entirely), but this has the distinct disadvantage that it will only work for normal rotations and will need to be changed depending on each specific situation it's used in.
Oh and, if you are now thinking that flash was a lot easier, believe me, the way the axis are rotated in HTML/CSS make a lot of sense and if you want flash like behavior use this library.
1 This is what Mohamed Ali is doing in his answer for example (the transformOffset function in his jsFiddle).
Disclaimer, it has been awhile since I have been doing this stuff and my understanding of matrices has never been extremely good, so if you see any mistakes, please do point them out/fix them.
For Webkit only, the webkitConvertPointFromPageToNode function handles the missing behavior:
var point = webkitConvertPointFromPageToNode(
document.getElementById("outer"),
new WebKitPoint(event.pageX, event.pageY)
);
jsFiddle: http://jsfiddle.net/kB4ra/108/
To cover other browsers as well, you can use the method described in this StackOverflow answer: https://stackoverflow.com/a/6994825/638544
function coords(event, element) {
function a(width) {
var l = 0, r = 200;
while (r - l > 0.0001) {
var mid = (r + l) / 2;
var a = document.createElement('div');
a.style.cssText = 'position: absolute;left:0;top:0;background: red;z-index: 1000;';
a.style[width ? 'width' : 'height'] = mid.toFixed(3) + '%';
a.style[width ? 'height' : 'width'] = '100%';
element.appendChild(a);
var x = document.elementFromPoint(event.clientX, event.clientY);
element.removeChild(a);
if (x === a) {
r = mid;
} else {
if (r === 200) {
return null;
}
l = mid;
}
}
return mid;
}
var l = a(true),
r = a(false);
return (l && r) ? {
x: l,
y: r
} : null;
}
This has the disadvantage of not working when the mouse is outside of the target element, but it should be possible to extend the area it covers by an arbitrary amount (though it would be rather hard to guarantee that it covers the entire window no matter how large).
jsFiddle: http://jsfiddle.net/kB4ra/122/
This can be extended to apply to #point by adding a mousemove event:
$('#outer').mousemove(function(event){
var point = convertCoordinates(event, $("#outer"));
$("#point").css({left: point.x+1, top: point.y+1});
});
Note that I adjust the x and y coordinates of #point by 1px to prevent it from being directly underneath the mouse; if I didn't do that, then it would block dragging #inner. An alternative fix would be to add handlers to #point that detect mouse events and pass them on to whichever element is directly underneath #point (and stopPropagation, so that they don't run twice on larger page elements).
jsFiddle: http://jsfiddle.net/kB4ra/123/
It seems to me that if you do not rotate the div, the div exactly follows the mouse.
This might be a problem with the plugin..maybe you could simulate the draggable function corretly?
This basically will do what you need though it is buggy. Bind the drag event handler, intercept the ui object and modify it to use the offset X and Y of the parent element. All of the X, Y, top, left etc. are in those objects. I will try to get you a better example sometime when today when I get a bit more time. Good luck!
http://jsfiddle.net/kB4ra/107/
may be this is issue of your jquery library or you can check this by assigning z-order value of inner div and outer div make sure that you give higher number to inner div.