将svg文本修剪(带省略号)到给定的像素宽度

时间:2019-02-27 03:43:11

标签: reactjs typescript svg text ellipsis

我有一个svg rect和一个使用React创建的svg文本。文本可以是短的也可以是很长的,如果宽度大于阈值,我想简单地用省略号修饰文本。

我知道对此有很多疑问(例如this one),但我仍无法解决问题。

这是我的代码:

Band Names That Are Ridiculously Long

这是结果:

enter image description here

您可以看到Band Names That Are Ridiculously...太长,所以我想要类似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> ) } } 的东西。

我尝试使用 React Ref

textObj

在这种情况下,timestamp with timezone为空...

我尝试的最后一个选择是使用textPath,但是在那种情况下,我将仅修剪字符串,而没有最后3个点,因此我不很喜欢将其作为解决方案。

如果有人帮助我,我会很高兴,因为我不知道该怎么做。 非常感谢

2 个答案:

答案 0 :(得分:2)

textObj在渲染方法中为null。 您必须在componentDidMount()中使用它。然后,创建一个ref数组,否则该ref将被循环中的每个元素覆盖。

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>
      // ...
    )
  }
}

答案 1 :(得分:0)

我有同样的问题。我尝试了梦游提出的方法,但对我没有用。我使用了与钩子api,useRef和useLayoutEffect略有不同的方式。所以我想出了另一个在svg中使用foreignObject的解决方案。这样,我们可以使用标准的html和css解决方案。

  <foreignObject x={0} y={0} width={maxTextwidth} height={20}>
    <div style={{textOverflow: 'ellipsis', with: maxTextwidth, overflow: 'hidden', whiteSpace: 'nowrap'}}>
      Long text to use ellipsis on
    </div>
  </foreignObject>