要在我的应用程序中呈现某些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>
);
}
}
Run Code Online (Sandbox Code Playgroud)
可用于一次测量多个元素:
<MeasureSvgElements onChange={onChange}>
{['Hello', 'Stack', 'Overflow'].map(str => <text>{str}</text>)}
</MeasureSvgElements>
Run Code Online (Sandbox Code Playgroud)
onChange 尺寸准备好后将被调用.
现在,我不确定使用<MeasureSvgElements>提供<svg>的尺寸渲染包含的最佳方法是什么onChange.
或者,有更好的方法吗?
一种解决方案可能是在渲染之前模拟每个text元素,收集高度和宽度(以及位置),然后迭代尺寸以确定应应用于父级的尺寸。
工作CodeSandbox: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"
}
];
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
这将在创建每个元素时被调用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;
};
Run Code Online (Sandbox Code Playgroud)
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>
);
});
Run Code Online (Sandbox Code Playgroud)
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]);
Run Code Online (Sandbox Code Playgroud)
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``;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
735 次 |
| 最近记录: |