我正在编写一个使用 React 生成 SVG 的可视化应用程序。我需要的部分之一是标签——即文本,由一个封闭框包围,带有可变文本,可能会旋转和设置样式。
所以我有一个组件NodeLabel
,目前具有固定尺寸:
render() {
return <g>
<rect className="label" x={this.props.x} y={this.props.y-10} width={20} height={40}></rect>
<text className="labelText" x={this.props.x} y={this.props.y}>{this.props.children}</text>
</g>
}
我在 DOM 中找到了一些关于这样做的信息,这里:SVG 文本周围的矩形边框
但我不太明白如何将其转换为 React 组件 - 在 render() 方法内部,没有可查看的 DOM 元素。我可以直接使用document.createElement()
并期望 SVG 元素的尺寸表现正常(并尊重 CSS)吗?另外,有没有办法避免创建代码的两个副本,一个在 JSX 中,一个在此之前计算尺寸?(例如,为这个临时的屏幕外副本评估 JSX 的片段到 DOM 元素)
更新:2018 年 1 月,我又回来了 :-) 实际应用程序是一个开源网络图表工具,目前使用 GD 和 PHP,但我希望转向 JS、React 和 SVG。
这里的带宽标签是我试图重现的,尽管节点标签在当前非 SVG 版本中使用相同的功能。
这是我的新最小示例:
// MyLabel should be centred at x,y, rotated by angle,
// and have a bounding box around it, 2px from the text.
class MyLabel extends React.Component {
render() {
const label = <text x={this.props.x} y={this.props.y} textAnchor="middle" alignmentBaseline="central">{this.props.children}</text>;
// label isn't a DOM element, so you can't call label.getBoundingClientRect() or getBBox()
// (Magic happens here to find bbox of label..)
// make up a static one for now
let bb = {x: this.props.x-20, y: this.props.y-6, width: 40, height: 12};
// add margin
const margin = 2;
bb.width += margin * 2;
bb.height += margin * 2;
bb.x -= margin;
bb.y -= margin;
// rect uses bbox to decide its size and position
const outline = <rect x={bb.x} y={bb.y} width={bb.width} height={bb.height} className="labeloutline"></rect>;
const rot = `rotate(${this.props.angle} ${this.props.x} ${this.props.y})`;
// build the final label (plus an x,y spot for now)
return <g transform={rot}>{outline}{label}<circle cx={this.props.x} cy={this.props.y} r="2" fill="red" /></g>;
}
}
class Application extends React.Component {
render() {
return <svg width={300} height={300}>
<MyLabel x={100} y={100} angle={0}>Dalmation</MyLabel>
<MyLabel x={200} y={100} angle={45}>Cocker Spaniel</MyLabel>
<MyLabel x={100} y={200} angle={145}>Pug</MyLabel>
<MyLabel x={200} y={200} angle={315}>Pomeranian</MyLabel>
</svg>;
}
}
/*
* Render the above component into the div#app
*/
ReactDOM.render(<Application />, document.getElementById('app'));
body { background: gray; }
svg {background: lightgray;}
.labeloutline { fill: white; stroke: black;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>