关于reactjs:修剪(带省略号)svg文本到给定的像素宽度

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

结果如下:

enter image description here

如您所见,Band Names That Are Ridiculously Long太长,所以我想要类似Band Names That Are Ridiculously...的东西。

我尝试使用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>
    )
  }
}

在这种情况下textObj为null ...

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

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


textObj在渲染方法中为null。
您必须在componentDidMount()中使用它。 然后,创建一个ref数组,否则该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
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>