Trim (with ellipsis) svg text to a given pixel width
我有一个svg rect和一个使用React创建的svg文本。 文本可以是短的也可以是很长的,如果宽度大于阈值,我想简单地将其修剪(用省略号)。
我知道对此有很多疑问(例如这个问题),但我尚未能够解决问题。
这是我的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | interface Props extends InterfaceState { rectWidth: number spaceBetweenRects: number } export class Labels extends React.Component<Props> { render() { const { rectWidth, labelRectHeight, spaceBetweenRects } = this.props const names = ['Cher', 'Madonna', 'Band Names That Are Ridiculously Long', 'U2'] const rectHeight = labelRectHeight const paddingLeftRight = 0.05 * rectWidth const plusWidth = rectHeight / 2 return ( <g> {names.map((name, i) => { const y = i * labelRectHeight + i * spaceBetweenRects const maxTextwidth = rectWidth - plusWidth - 3 * paddingLeftRight // this is the maximum length that text can have return ( <g key={i}> <React.Fragment> <rect x={0} y={y} width={rectWidth} height={rectHeight} fill={'#E4E9EB'} /> </React.Fragment> <text ref={this.textRef} x={rectWidth - paddingLeftRight} y={y + rectHeight / 2} textAnchor="end" alignmentBaseline="middle" > {name} </text> </g> ) })} </g> ) } } |
结果如下:
如您所见,
我尝试使用React Ref:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | export class Labels extends React.Component<Props> { textRef = React.createRef<SVGTextElement>() /** * Places textString in textObj and adds an ellipsis (...) if text can't fit in width. * TODO: type of textObject (RefObject<SVGTextElement> doesn't work) */ placeTextWithEllipsis(textObj: any, textString: string, width: number) { console.log('textObj: ', textObj) // null :() textObj.textContent = textString if (textObj.getSubStringLength(0, textString.length) >= width) { for (let x = textString.length - 3; x > 0; x -= 3) { if (textObj.getSubStringLength(0, x) <= width) { textObj.textContent = textString.substring(0, x) + '...' return } } textObj.textContent = '...' // can't place at all } } render() { const { rectWidth, labelRectHeight, spaceBetweenRects } = this.props const names = ['Cher', 'Madonna', 'Band Names That Are Ridiculously Long', 'U2'] const rectHeight = labelRectHeight const paddingLeftRight = 0.05 * rectWidth const plusWidth = rectHeight / 2 return ( <g> {names.map((name, i) => { const y = i * labelRectHeight + i * spaceBetweenRects const maxTextwidth = rectWidth - plusWidth - 3 * paddingLeftRight // this is the maximum length that text can have this.placeTextWithEllipsis(this.textRef, name, maxTextwidth) return ( <g key={i}> <React.Fragment> <rect x={0} y={y} width={rectWidth} height={rectHeight} fill={'#E4E9EB'} /> </React.Fragment> <text ref={this.textRef} x={rectWidth - paddingLeftRight} y={y + rectHeight / 2} textAnchor="end" alignmentBaseline="middle" > {name} </text> </g> ) })} </g> ) } } |
在这种情况下
我尝试的最后一个选择是使用textPath,但是在那种情况下,我将只修剪字符串,而没有最后3个点,因此我不太喜欢它作为解决方案。
如果有人帮助我,我会很高兴,因为我不知道该怎么做。
非常感谢
您必须在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | export class Labels extends React.Component<Props> { textRefs = new Array(React.createRef<SVGTextElement>()) componentDidMount() { const { maxTextwidth } = this.props this.textRefs.forEach((ref, i) => { placeTextWithEllipsis( this.textRefs[i].current, this.textRefs[i].current.innerHTML, maxTextwidth ) }) } render() { return ( // ... <text ref={(this.textRefs[i] = React.createRef<SVGTextElement>())} x={rectWidth - paddingLeftRight} y={y + rectHeight / 2} textAnchor="end" alignmentBaseline="middle" > {...} </text> // ... ) } } |
我也有同样的问题。 我尝试了梦游提出的方法,但对我没有用。 我使用了与钩子api,useRef和useLayoutEffect略有不同的方式。 所以我想出了另一个在svg中使用foreignObject的解决方案。 这样,我们可以使用标准的html和css解决方案。
1 2 3 4 5 | <foreignObject x={0} y={0} width={maxTextwidth} height={20}> Long text to use ellipsis on </foreignObject> |