react-xarrows
react-xarrows copied to clipboard
offset anchors towards facing direction
Is your feature request related to a problem? Please describe.
When a straight arrow is anchored to a circular/round element, neither middle nor auto is a preferable choice.
Describe the solution you'd like
It would be nice if there were a variant of middle where you could provide a radius offset where the arrow would point towards the middle of the target element, but is radius-amount shorter.
Of course, this type of anchor would only really be useful on straight arrows.
Problem

(emulated) Desired Outcome

please refer to custom svg it the repo docs.
type headShapeType<T extends svgElemType> = {
svgElem: SVGElementTagNameMap[T];
offsetForward?: number;
};
try:
import { arrowShapes } from 'react-xarrows';
const customSvg = {
svgElem: arrowShapes.arrow1;
offsetForward: 1; //change
};
then change customSvg.offsetForward and pass it down to xarrow.
does this solves your issue?
Oh! Didn't catch that part of the docs. My bad. This almost fixes the issue. If I set the offsetForward to some negative value, I get this result. Not excactly the desired effect, but very close. How would I get the arrow head to follow the line?
import Xarrow, {arrowShapes} from "react-xarrows";
const customSvg = {
svgElem: arrowShapes.arrow1.svgElem,
offsetForward: -1
};
you could extend the line using offset option in the anchors. foe example const endAnchor= { position: 'auto'; offset: { x: 20,y:0 }; };
currently, you can only offset only with x and y but soon there will be an option to offset relative to the facing direction.
position 'auto' does not work for me, since I need the arrow to point towards the middle.
currently, you can only offset only with x and y but soon there will be an option to offset relative to the facing direction.
That is excactly what I described and have implemented in the PR. Technically I would be able to calculate the x and y offsets using the same approach in my own application, but it would be nice if it was available as a library feature.
understood, you are right and this is planned for the next release. will leave this issue open as a feature request until the next release that solves this.
For anyone interested, this is how I ended up sidestepping this issue (please excuse the messy code) note that this code is not very performant:
import React, {Component} from "react";
import Xarrow from "react-xarrows";
import * as ReactDOM from "react-dom";
class Edge extends Component {
state = {
startOffset: {},
endOffset: {}
}
dist(a, b) {
return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2);
}
calculateOffset(startId, endId) {
let start_el = document.getElementById(startId);
let end_el = document.getElementById(endId);
if(start_el === null || end_el === null)
return {x:0, y:0};
let start_box = start_el.getBoundingClientRect();
let end_box = end_el.getBoundingClientRect();
let start = {x: start_box.x + start_box.width/2, y: start_box.y + start_box.height/2};
let end = {x: end_box.x + end_box.width/2, y: end_box.y + end_box.height/2};
let radius = end_box.width / 2;
let directionMagnitude = this.dist(start, end);
if(directionMagnitude === 0)
return {x:0, y:0};
let directionNormalized = {
x: (start.x - end.x) / directionMagnitude,
y: (start.y - end.y) / directionMagnitude
};
return {
x: (directionNormalized.x * radius),
y: (directionNormalized.y * radius)
};
}
updateOffsets = () => {
const startOffset = this.props.startAnchor === 'middle' ? this.calculateOffset(this.props.end, this.props.start) : {x: 0, y: 0};
const endOffset = this.props.endAnchor === 'middle' ? this.calculateOffset(this.props.start, this.props.end) : {x: 0, y: 0};
this.setState({
startOffset: startOffset,
endOffset: endOffset
});
}
componentDidMount() {
// TODO: Once react-xarrows supports anchors with offset in facing direction, use that instead.
ReactDOM.findDOMNode(this).addEventListener("DOMAttrModified", this.updateOffsets.bind(this));
}
render() {
const {start, end, id, startAnchor, endAnchor} = this.props;
return (
<Xarrow start={start}
end={end}
path='straight'
startAnchor={{
position: startAnchor,
offset: this.state.startOffset
}}
endAnchor={{
position: endAnchor,
offset: this.state.endOffset
}}
/>
);
}
}
export default Edge;