如何在React中测量SVG元素?

10
为了在我的应用程序中呈现某些SVG元素,我需要首先测量一些其他SVG元素。
例如,想象一个
1个回答

2

一种解决方案可能是在渲染之前模拟每个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;
  };

创建文本元素的函数:

  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>
      );
    });

使用useEffect来检测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``;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接