【问题标题】:How to measure SVG elements in React?如何在 React 中测量 SVG 元素?
【发布时间】:2017-10-08 09:10:15
【问题描述】:

要在我的应用程序中渲染某些 SVG 元素,我需要先测量一些其他 SVG 元素。

例如,想象一个<text> 元素随机定位在正方形 (0,0) - (100,100) 中,并且可以有各种字体大小、字体系列等。

如果文本位于 (10,20),宽度为 30,高度为 40,我想将包含 <svg> 的宽度设置为 40 (= 10 + 30),高度设置为60 (= 20 + 40)。

重点是:<text> 需要在渲染<svg>之前进行测量。

为了帮助<text> 测量,我创建了以下组件:

class MeasureSvgElements extends React.Component {
  storeSvgReference = svg => {
    if (svg !== null) {
      this.svg = svg;
    }
  };

  measure() {
    const childElements = Array.from(this.svg.children);
    const dimensions = childElements
      .map(element => element.getBoundingClientRect())
      .map(({ width, height }) => ({ width, height }));

    this.props.onChange(dimensions);
  }

  componentDidMount() {
    this.measure();
  }

  componentDidUpdate() {
    this.measure();
  }

  render() {
    return (
      <svg width="0" height="0" style={{ display: 'block' }} ref={this.storeSvgReference}>
        {this.props.children}
      </svg>
    );
  }
}

可用于一次测量多个元素:

<MeasureSvgElements onChange={onChange}>
  {['Hello', 'Stack', 'Overflow'].map(str => <text>{str}</text>)}
</MeasureSvgElements>

一旦尺寸准备好,将调用onChange

现在,我不确定使用&lt;MeasureSvgElements&gt; 使用onChange 提供的尺寸呈现包含&lt;svg&gt; 的最佳方法是什么。

或者,有没有更好的方法?

【问题讨论】:

    标签: reactjs svg


    【解决方案1】:

    一种解决方案可能是在渲染之前模拟每个text 元素,收集高度和宽度(除了位置),然后遍历尺寸以确定应该将哪些尺寸应用于父元素。

    工作代码沙盒:https://codesandbox.io/s/stack-43880276-dynamic-svg-textbox-id3rt

    数据结构:

    import React, { useState, useEffect } from "react";
    import styled from "styled-components";
    
    const yourTextData = [
      {
        id: 0,
        posX: 0,
        posY: 0,
        str: "~~Hello~~",
        fontFamily: "URW Chancery L, cursive",
        fontSize: "30"
      },
      {
        id: 1,
        posX: 20,
        posY: 20,
        str: "~~Stack~~",
        fontFamily: "URW Chancery L, cursive",
        fontSize: "30"
      },
      {
        id: 2,
        posX: 40,
        posY: 40,
        str: "~~Overflow~~",
        fontFamily: "URW Chancery L, cursive",
        fontSize: "30"
      },
      {
        id: 3,
        posX: 0,
        posY: 80,
        str: "This SVG with text sets its own",
        fontFamily: "Arial, sans-serif",
        fontSize: "30"
      },
      {
        id: 4,
        posX: 40,
        posY: 120,
        str: "d i m e n s i o n s",
        fontFamily: "FreeMono, monospace",
        fontSize: "30"
      }
    ];
    

    定义你的组件和钩子:

    const DynamicSVGText = (props) => {
      const [svgWidth, setSvgWidth] = useState(0);
      const [svgHeight, setSvgHeight] = useState(0);
      const [textDims, setTextDims] = useState([]); // array of objects, defining dims/pos for each texteach text
    
    

    测量功能

    这将在创建每个 text 元素时调用并保存到 textDims 挂钩。

      const measure = (data) => {
        // create a mock element
        let newText = document.createElement("text");
        newText.setAttribute("id", data.id);
        document.body.appendChild(newText);
    
        // append text data
        let theTextEle = document.getElementById(`${data.id}`);
        theTextEle.innerHTML += data.str;
    
        // append text font / size, bc might as well be fancy
        theTextEle.style.fontFamily = data.fontFamily;
        theTextEle.style.fontSize = `${data.fontSize}px`;
    
        // measure element
        let width = theTextEle.getBoundingClientRect().width;
        let height = theTextEle.getBoundingClientRect().height;
    
        // delete element
        theTextEle.parentNode.removeChild(theTextEle);
    
        // set data
        let dimData = [width, height];
        //console.log(dimData);
    
        // return dimension data
        return dimData;
      };
    

    创建text元素的函数:

      const SvgText = ({ text }) =>
        text.map((data, i) => {
          let dimensions = measure(data);
    
          let updatedTextDims = textDims;
    
          updatedTextDims[data.id] = {
            x: data.posX,
            y: data.posY,
            w: dimensions[0],
            h: dimensions[1]
          };
    
          return (
            <StyledText
              fontFamily={data.fontFamily}
              fontSize={data.fontSize}
              key={data.id}
              x={data.posX.toString()}
              y={(data.posY + dimensions[1]).toString()}
            >
              {data.str}
            </StyledText>
          );
        });
    

    使用Effect 检测textDims 挂钩何时发生变化

      useEffect(() => {
        let tw = 0; // longest width
        let th = 0; // tallest height
    
        // loop through text elements and their dimensions,
        // set total width/height to the greatest dimensions across objects
        for (let d = 0; d < textDims.length; d++) {
          let thisWidth = textDims[d].x + textDims[d].w;
          let thisHeight = textDims[d].y + textDims[d].h;
    
          if (thisWidth > tw) {
            tw = thisWidth;
          }
    
          if (thisHeight > th) {
            th = thisHeight;
          }
        }
    
        setSvgWidth(tw);
        setSvgHeight(th);
      }, [textDims]);
    

    完成组件,以及一点样式

      return (
        <svg
          width={svgWidth.toString()}
          height={svgHeight.toString()}
          style={{ display: "block" }}
        >
          <SvgText text={props.text} />
        </svg>
      );
    };
    
    export default function App() {
      return (
        <Container>
          <DynamicSVGText text={yourTextData} />
        </Container>
      );
    }
    
    const Container = styled.div`
      display: flex;
      flex-direction: row;
      justify-content: center;
    `;
    
    const StyledText = styled.text``;
    

    【讨论】:

      猜你喜欢
      • 2022-01-11
      • 2019-01-13
      • 2019-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-03
      • 2016-01-16
      • 1970-01-01
      相关资源
      最近更新 更多