用 React 计算 SVG 边界框?

2024-01-22

我正在编写一个使用 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 文本周围的矩形边框 https://stackoverflow.com/questions/17218108/rectangle-border-around-svg-text

但我不太明白如何将其转换为 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>

您可以预先计算/测量字体几何形状,并根据输入字符串获得文本尺寸的合理估计(这是最简单的解决方案,但如果字体发生变化,显然会破坏),或者执行两阶段渲染:

也就是说,您通过 ref 获取 dom 元素,在挂载上获取框,最后通过更新状态重新渲染,如下所示:

class MyLabel extends React.Component {
  constructor(props){
    super(props);
    this.state = {text_extents:null};
  }
  componentDidMount() {
   const box = this.text.getBBox();

   this.setState({text_extents:[box.width,box.height]});
  }
 render() {
   const margin = 2;
   const extents = this.state.text_extents;
   const label = <text ref={(t) => { this.text = t; }} textAnchor="middle" dy={extents?(extents[1]/4):0} >{this.props.children}</text>;
   const outline = extents ?
         <rect x={-extents[0]/2-margin} y={-extents[1]/2-margin} width={extents[0]+2*margin} height={extents[1]+2*margin} className="labeloutline"></rect>
         : null;

   return <g transform={`translate(${this.props.x},${this.props.y}) rotate(${this.props.angle})`}>{outline}{label}</g>;
 }
}

请注意,根据最新的反应文档,这不应导致任何用户可见的闪烁:

组件DidMount():在此方法中调用 setState() 将触发额外的渲染,但它会在浏览器更新屏幕之前发生。这保证了即使在这种情况下 render() 被调用两次,用户也不会看到中间状态。请谨慎使用此模式,因为它通常会导致性能问题。然而,对于模态和工具提示等情况,当您需要在渲染依赖于其大小或位置的内容之前测量 DOM 节点时,它可能是必要的。

最后,请注意,如果标签字符串发生更改(通过 props 或其他方式),您将需要相应地更新范围(通过componentDidUpdate()).

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用 React 计算 SVG 边界框? 的相关文章

  • 动态速度计 javascript 或 jquery 插件

    我希望有动态ajax插件在页面上显示速度计 一个想法是我设置一个背景并旋转针 有人知道相关插件吗 这里有一些供您参考 http bernii github com gauge js http bernii github com gauge
  • 使用 React.lazy 时未捕获未定义的错误

    我正在尝试实施基于路由的代码分割 https reactjs org docs code splitting html route based code splitting正如 React 文档中提到的 这是添加延迟实现之前我的应用程序 这
  • Node js 使用中间件重定向进行过多重定向

    在我的 Node js 应用程序 我使用的是express 4 x 中 我想检查用户是否已登录 如果用户未登录 我想重定向到我的登录页面 然后我在中间件中这样做 服务器 js app use function req res next if
  • 消息“在 jest.setTimeout 指定的 5000 毫秒超时内未调用异步回调”

    我正在使用 Puppeteer 和 Jest 来运行一些前端测试 我的测试如下 describe Profile Tab Exists and Clickable settings user gt test Assert that you
  • 动画进度元素值

    我有一个progress元素 该元素如下所示 div class container div div div
  • 如何使用canvas.toDataURL()将画布保存为图像?

    我目前正在构建一个 HTML5 Web 应用程序 Phonegap 本机应用程序 我似乎不知道如何将画布保存为图像canvas toDataURL 有人可以帮我吗 这是代码 有什么问题吗 我的画布被命名为 canvasSignature J
  • Ember.js 处理 View 事件后转换到路由

    Setup 我有一个 Ember 应用程序 支持使用 Imgur API 上传图像 我已经有一个工作路线和模板来处理任何 Imgur ID 但我想在上传新图像后转换到此路线 使用返回的 Imgur ID 这是该应用程序的相关部分 http
  • Leaflet js虚构地图

    我是 Leaflet 的新手 我想了解如何创建完全交互式的虚构地图 我有一张图像想要转换为传单地图 该图像基本上像图表一样具有许多连接和点 我想首先将该图像转换为地图 能够将鼠标悬停在这些点上 突出显示它们并显示有关它们的信息 并且还可以在
  • React Native:加载图像后应用程序性能不佳

    加载图像似乎没有问题 但是加载完毕后就出现问题了 在我的应用程序中 我在整个游戏中一张一张地加载卡片图像 一旦我加载了 40 张卡片图像 整个应用程序就会变得很慢 它总是发生在第 40 个图像处 当我在第 40 个图像之后继续加载更多卡片图
  • 如果链接包含特定文本,jQuery 将类添加到 href

    我的网站上的列表中有一些动态填充的链接 这些链接链接到文件 是否可以使用 jQuery 查看文件名是否以 pdf 结尾 并在 href 或类似的链接文本以 mp3 结尾时添加一个类 例如 我的列表中有以下链接 文件1 pdf 歌曲1 mp3
  • JS用正则表达式替换数字

    我有元素的标识符 如下所示 form book 1 2 3 我想要的是用其他值替换该标识符中的第二个数字 我将函数 match 与以下正则表达式一起使用 var regexp d d d 但它返回我包含的数组 1 2 3 2 因此 当我尝试
  • 淡出和循环一组 div 的最佳方式

    假设我有以下 div div class a You are funny div div class b You are smart div div class c You are cool div 最好的展示方式是什么div a持续 5
  • Chartjs刻度标签位置

    尝试让 Y 轴刻度标签看起来像image https i stack imgur com XgoxX png 位于秤顶部且不旋转 缩放选项当前如下所示 scales yAxes id temp scaleLabel display true
  • 如果数字小于 10,则显示前导零 [重复]

    这个问题在这里已经有答案了 可能的重复 JavaScript 相当于 printf string format https stackoverflow com questions 610406 javascript equivalent t
  • 如何将函数内的捕获错误传递给父级

    我有这几行代码示例 想知道下面的逻辑到底如何 try var response child console log why here catch err console log should show this err function c
  • 如何在 Google 地图 V3 中创建编号地图标记?

    我正在制作一张上面有多个标记的地图 这些标记使用自定义图标 但我还想在顶部添加数字 我已经了解了如何使用旧版本的 API 来实现这一点 我怎样才能在V3中做到这一点 注意 当您将鼠标悬停在标记上时 标题 属性会创建一个工具提示 但我希望即使
  • 使用 Flask/WTForms 和 React 进行 CSRF 保护

    有没有人成功地为使用 React 作为受控组件 提交到 Flask 后端 最好使用 WTForms 的表单实现了 CSRF 保护 我看过很多部分答案 其中一个是关于 Django 的 但找不到任何关于 Flask 的明确答案 我的大问题似乎
  • 在 React Native 中将 Props 传递到屏幕

    我已经开始学习 React Native 并且一如既往地从创建可重用组件开始 我了解了如何在创建自定义组件时传递和访问 props 我想在 React Native 中创建一个基本屏幕 它具有通用属性 并且我的应用程序中的所有屏幕都可以设置
  • 单击列表时使用 bootstrap Dropdown 防止下拉菜单消失

    我正在使用使用引导下拉菜单 http twitter github com bootstrap javascript html dropdowns生成下拉菜单 我想防止点击菜单时菜单消失 我已经实现了以下代码 但它不起作用 知道如何修复它吗
  • 用于 C# XNA 的 Javascript(或类似)游戏脚本

    最近我准备用 XNA C 开发另一个游戏 上次我在 XNA C 中开发游戏时 遇到了必须向游戏中添加地图和可自定义数据的问题 每次我想添加新内容或更改游戏角色的某些值或其他内容时 我都必须重建整个游戏或其他内容 这可能需要相当长的时间 有没

随机推荐