web-dev-qa-db-fra.com

Ajouter des ellipses au texte en débordement dans SVG?

J'utilise D3.js. Je voudrais trouver un équivalent SVG à cette classe CSS, qui ajoute des ellipses si le texte sort de sa div contenant:

.ai-Ellipsis {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: Ellipsis;
  -o-text-overflow: Ellipsis;
  -moz-binding: url(<q>assets/xml/Ellipsis.xml#Ellipsis</q>);
}

C'est mon SVG: 

<g class="bar" transform="translate(0,39)">
    <text class="label" x="-3" y="6.5" dy=".35em" text-anchor="start">Construction</text>    
    <rect height="13" width="123"></rect>
</g>

Il est généré comme suit: 

barEnter.append("text").attr("class", "label")
        .attr("x", -3).attr("y", function() { return y.rangeBand() / 2})
        .attr("dy", ".35em").attr("text-anchor", "start")
        .text(function(d) {
            return d.Name;
        });

Actuellement, le texte déborde et chevauche l'élément rect. 

Est-il possible de dire "si le texte dépasse une certaine largeur, rognez-le et ajoutez des ellipses"?

24
Richard

Je ne connais pas de classe CSS équivalente pour SVG, mais vous pouvez utiliser foreignObject pour incorporer du code HTML dans SVG. Cela vous donne accès à cette fonctionnalité et est plus flexible en général (par exemple, vous pouvez facilement faire des sauts de ligne automatiques).

Voir ici pour un exemple complet.

14
Lars Kotthoff

une fonction wrapper pour le texte en débordement:

    function wrap() {
        var self = d3.select(this),
            textLength = self.node().getComputedTextLength(),
            text = self.text();
        while (textLength > (width - 2 * padding) && text.length > 0) {
            text = text.slice(0, -1);
            self.text(text + '...');
            textLength = self.node().getComputedTextLength();
        }
    } 

usage:

text.append('tspan').text(function(d) { return d.name; }).each(wrap);
54
user2846569

J'ai implémenté une fonction native qui ne dépend pas de d3, cette fonction implémente des solutions de repli à 3 voies:

function textEllipsis(el, text, width) {
  if (typeof el.getSubStringLength !== "undefined") {
    el.textContent = text;
    var len = text.length;
    while (el.getSubStringLength(0, len--) > width) {}
    el.textContent = text.slice(0, len) + "...";
  } else if (typeof el.getComputedTextLength !== "undefined") {
    while (el.getComputedTextLength() > width) {
      text = text.slice(0,-1);
      el.textContent = text + "...";
    }
  } else {
    // the last fallback
    while (el.getBBox().width > width) {
      text = text.slice(0,-1);
      // we need to update the textContent to update the boundary width
      el.textContent = text + "...";
    }
  }
}
2
c9s

Juste une mise à jour sur la fonction d'emballage proposée par l'utilisateur2846569. getComputedTextLength () a tendance à être très lent, donc ... 

MODIFIER 

J'ai appliqué diligemment les conseils de user2846569 et en ai fait une version avec recherche "binaire", avec un calibrage et une précision paramétrés.

'use strict';

var width = 2560;

d3.select('svg').attr('width', width);

// From http://stackoverflow.com/questions/10726909/random-alpha-numeric-string-in-javascript
function randomString(length, chars) {
    var result = '';
    for (var i = length; i > 0; --i)
        result += chars[Math.floor(Math.random() * chars.length)];
    return result;
}

function wrap() {
    var self = d3.select(this),
        textWidth = self.node().getComputedTextLength(),    // Width of text in pixel.
        initialText = self.text(),                          // Initial text.
        textLength = initialText.length,                    // Length of text in characters.
        text = initialText,
        precision = 10, //textWidth / width,                // Adjustable precision.
        maxIterations = 100; // width;                      // Set iterations limit.

    while (maxIterations > 0 && text.length > 0 && Math.abs(width - textWidth) > precision) {

        text = /*text.slice(0,-1); =*/(textWidth >= width) ? text.slice(0, -textLength * 0.15) : initialText.slice(0, textLength * 1.15);
        self.text(text + '...');
        textWidth = self.node().getComputedTextLength();
        textLength = text.length;
        maxIterations--;
    }
    console.log(width - textWidth);
}

var g = d3.select('g');

g.append('text').append('tspan').text(function(d) {
    return randomString(width, 'a');
}).each(wrap);

Voir sur JSFiddle.

0
Mauro Colella