Open dexluce opened 7 years ago
That would be really nice, actually :)
Well, I wasn't able to do it by myself. onDrag and onResize doesn't allow the use of any function on the rnd element. For exemple, this.rnd.updatePosition(... ) won't work in onDrag or onResize. Changing "bounds" doesn't works neither.
So... I change my approach. I have implemented a 'magnet' onResizeStop and onDragStop. I juste write quickly the psedo-code. Magnet is only apply on X axe. I don't have the time know but let my know and I'll write a more usable code then this:
var allDivBorderYouWantToMagnetize = {}
var magneticPos;
onDragStart(() => {
//here, you want to keep all x magnetic value as key in an object.
forEach(rnd in rndElements) {
this.allDivBorderYouWantToMagnetize[rnd.xPosition].push(rnd)
}
})
onDrag((rnd) => {
Object.keys(this.allDivBorderYouWantToMagnetize).forEach((key) => {
//magnet will be activated if you are 5px around another rnd element
if (rnd.x - key > -5 && rnd.x - key < 5)
this.magneticPos = key;
else
this.magneticPos = event.xPos;
})
})
onDragStop(() => {
this.rnd.setPosition({x: this.magneticPos})
})
With that approach, the object only gets moved into its place after you've finished dragging. I'm trying to accomplish the same thing, but also while you're dragging the object (no luck yet).
I would need something like "return false" on "onDrag" to cancel the movement, or a way to access the React object inside "onDrag" to call setPosition if the overlap condition is met.
Hi guys, any updates on this?
您好,我想问一下防止rnd重叠现在有实现吗
Hello! Did anyone find a way to solve this problem? I'm in need of the exact same thing
Hi, here is my proposal; for 2 Rnds for now - some issue with more than 2 - i suspect issue with the loop. https://codesandbox.io/s/react-rnd-test-lcp7b
Call this method whenever you need to check for collision. In my demo im running it when onDragStop( ) and onResizeStop( ) are triggered.
function overlaps() {
//get all draggables in the doc - should be done using refs etc in react
var draggables = document.getElementsByClassName("react-draggable");
//iterate through all of them checking overlap.
Array.from(draggables).map((item, e) => {
Array.from(draggables).map((oItem, e) => {
if (item !== oItem) {
const rect1 = item.getBoundingClientRect();
const rect2 = oItem.getBoundingClientRect();
const isInHoriztonalBounds = rect1.x < rect2.x + rect2.width && rect1.x + rect1.width > rect2.x;
const isInVerticalBounds = rect1.y < rect2.y + rect2.height && rect1.y + rect1.height > rect2.y;
var isOverlapping = isInHoriztonalBounds && isInVerticalBounds;
//right here you know the two components that are overlapping
//resolve overlap the way best fits your project.
//setting state.
setCollision(isOverlapping);
}
});
});
}
This code handles collision detection and restoration to the last known "safe point" or default position when drag ends (if there is a collison of the elements).
/* This will handle our overlap calculations
In my code, I have this above component that handles single node */
function haveIntersection(other, main) {
return !(
main.x > other.x + other.width ||
main.x + main.width < other.x ||
main.y > other.y + other.height ||
main.y + main.height < other.y
);
}
/*
* mn872 is a class of each nested div, that holds text / icons
* Structure looks like this map > div.wrap > Rnd > div.wrap2 > div.mn872 > .... (simplified)
* We have to use .some() as it is not possible to stop forEach loop
* We need to stop loop, because next element would override the state to false
* You can also use .every(), but in that case you will flip return values
* setIsCollision() is just regular useState(bool)
*/
const [isCollision, setIsCollision] = useState(false);
function handleOverlap(node, xy) {
const main = node?.querySelector(".mn872"); // current dragged or resized node
const targetRect = main?.getBoundingClientRect();
[...document.querySelectorAll(".mn872")].some((group) => {
if (group === main) return; // continue with a loop if the current element is inside the group
if (haveIntersection(group.getBoundingClientRect(), targetRect)) {
setIsCollision(true);
return true; // current element is overlapping - stop loop
}
setIsCollision(false);
setSafePoint(xy); // remove this line if you want to snap to initial position
return; // continue with a loop - current element is NOT overlapping
});
}
const [safePoint, setSafePoint] = useState({ x: 0, y: 0 });
function handleDragStart(e, { x, y }) {
setSafePoint({ x, y });
}
/* We use 1ms timeout as browser needs a tiny bit of time to have everything in sync.
On the other hand, we need to have correct values. Try it without timeout and you will see.
I call this function from Rnd component during "onDrag" event. It should work for other events too. */
function handleDrag(e, { node, x, y }) {
setTimeout(() => handleOverlap(node, { x, y }), 1);
}
function handleDragStop(e, { node: { clientWidth } }) {
if (isCollision) {
nodeRef.current.updatePosition(safePoint);
setIsCollision(false);
return;
}
//const width = clientWidth;
//nodeRef.current.updateSize({ width, height: TL_DEFUALT_HEIGHT });
const { x, y } = nodeRef.current.draggable.state;
nodeRef.current.updatePosition({ x, y });
}
Some parts token from https://konvajs.org/docs/sandbox/Collision_Detection.html
@Rados51 can you help with an example on codesandbox or anything else that would help out a newbie on react and react-rnd
@sandeepzgk
This is the most basic example (check console.log on collision) https://codesandbox.io/s/react-rnd-collision-example-4impqy?file=/src/index.js
If you need material to start working with React, I would recommend Scrimba
I would also really love this to be implemented.
I second that it'd be great to extend the bounds
prop to disallow overlapping
@Rados51 how do I get the nodeRef ??
@AdityaT-19 With useRef
from React.
But you can also have an external position and size state instead of directly updating its postion.
If needed, you can also get x/y state (originally from nodeRef.current.draggable.state
) via second argument (e: DraggableEvent, data: DraggableData
).
Hello,
I see a few way to restrict the overlaping of two rnd elements, but it would be nice to just set a boolean for that.
As I need this, could you tell me how you would do that? (Do I use onDrag callback to verify if overlap is happening and prevent it?)
Thank you very much for the nice work, regards