react-archer
react-archer copied to clipboard
Arrow direction issue
Issue
When you have two items where the source anchor is on the same x point as the target anchor, the arrow get pointed in the wrong direction.

<ArcherElement
id={String(event.id)}
relations={event.connections?.map(targetId => ({
targetId,
sourceAnchor: 'right',
targetAnchor: 'left',
}))}
key={event.id}
>
Hello!
Thank you for the issue! The lib needs many drawing enhancements indeed.
How would you expect the arrows to be drawn? In that case, I'm not sure what we'd expect.
In this case, I would expect to act something like this:

Mmmmh in terms of API I wouldn't expect { targetAnchor: 'left' } to give this result.
For such a result, this should be { targetAnchor: 'top' } since the arrow goes on top. Right now, this would give you a centered arrow, but one could imagine having a new offset prop that would allow you to put a continuous value between -1 and 1 that would shift the arrow alongside the border. For example here this would be { targetAnchor: 'top', targetAnchorLateralOffset: -0.8 }.
This is just a rough idea 🤔
Sure, but to be fair, this one has { sourceAnchor: 'right' } and still shows up on the bottom, nicely, so I don't see why the target couldn't 🤷♂️

Haha good point, but try putting down the opacity of your green block. I think you'll notice that the arrow goes backward, starting from the right border 😅
Ah I see 😄
Anyway, I think the package should solve this issue, and that it shouldn't be up to the user to try to find out exactly when the source and the target area on the same x position, and then manually have to set some sort of offset. Maybe you could add a prop to opt-out or into this though.
I resolved the arrow direction issue in myself.
https://user-images.githubusercontent.com/7702653/129482654-80363278-ddcb-42ca-818a-02e8b141dded.mp4
Commit: https://github.com/hata6502/troopa-wasm/commit/c305689ddb5010a678130db03695845c86ef4526
By using useEffect(), the arrow direction is updated every 200 ms.
useEffect(() => {
const handle = () =>
setRelations(
getRelations({
anchorlessRelations: [
...anchorlessRelations,
...(connectionCuror ? [{ targetId: cursorID }] : []),
],
sourceId: radioID,
})
);
handle();
const intervalID = setInterval(handle, 200);
return () => clearInterval(intervalID);
}, [anchorlessRelations, connectionCuror, cursorID, radioID]);
Then, detect arrow direction by getting DOMRects.
const arrowLength = 40;
const detectAnchors = ({
sourceCenterX,
sourceCenterY,
targetCenterX,
targetCenterY,
}: {
sourceCenterX: number;
sourceCenterY: number;
targetCenterX: number;
targetCenterY: number;
}): Pick<Relation, "sourceAnchor" | "targetAnchor"> => {
if (Math.abs(sourceCenterX - targetCenterX) >= arrowLength) {
return sourceCenterX < targetCenterX
? {
sourceAnchor: "right",
targetAnchor: "left",
}
: {
sourceAnchor: "left",
targetAnchor: "right",
};
}
if (Math.abs(sourceCenterY - targetCenterY) >= arrowLength) {
return sourceCenterY < targetCenterY
? {
sourceAnchor: "bottom",
targetAnchor: "top",
}
: {
sourceAnchor: "top",
targetAnchor: "bottom",
};
}
return {
sourceAnchor: "middle",
targetAnchor: "middle",
};
};
const getRelations = ({
anchorlessRelations,
sourceId,
}: {
anchorlessRelations: AnchorlessRelation[];
sourceId: string;
}) => {
const sourceElement = document.getElementById(sourceId);
if (!sourceElement) {
throw new Error(`Could not find element with id "${sourceId}"`);
}
const soruceDOMRect = sourceElement.getBoundingClientRect();
const sourceCenterX = soruceDOMRect.left + soruceDOMRect.width / 2;
const sourceCenterY = soruceDOMRect.top + soruceDOMRect.height / 2;
return anchorlessRelations.map((anchorlessRelation): Relation => {
const targetElement = document.getElementById(anchorlessRelation.targetId);
if (!targetElement) {
throw new Error(
`Could not find element with id "${anchorlessRelation.targetId}"`
);
}
const targetDOMRect = targetElement.getBoundingClientRect();
const targetCenterX = targetDOMRect.left + targetDOMRect.width / 2;
const targetCenterY = targetDOMRect.top + targetDOMRect.height / 2;
return {
...anchorlessRelation,
...detectAnchors({
sourceCenterX,
sourceCenterY,
targetCenterX,
targetCenterY,
}),
};
});
};