Вертикально центрировать метку, состоящую из нескольких строк, над узлом в графе D3, направленном силой.

У меня есть граф, ориентированный на силу D3, и каждый узел отображает метку. Длина этой метки может варьироваться от одной строки текста до нескольких строк текста. Я успешно создал многострочную метку для узлов, обратившись к этому пост. Однако я хотел бы вертикально центрировать метку над узлом. Мне не удалось переназначить положение метки по вертикальному центру таким образом, чтобы это было эффективно для всех типов меток (например, однострочные метки, двухстрочные метки и т. д.).

По сути, я не уверен, как центрировать метку в целом, сохраняя при этом разделение между каждой строкой текста в метке. Любая помощь будет оценена.

var node = svg.selectAll('.node')
    .data(force.nodes())
    .enter().append('circle')
    .attr('class', 'node')
    .attr('r', width * 0.01)
    .style('fill', function(d) {return color(d.group); })

var maxLength = 20;
var separation = 12;
var textX = 0;

var text = svg.selectAll(".text")
    .data(nodes)
    .enter().append("text")
    .style("text-anchor", "middle")
    .each(function (d) {
          var lines = wordwrap2(d.name, maxLength).split('\n');
          for (var i = 0; i < lines.length; i++) {
              d3.select(this)
                .append("tspan")
                .attr("dy", separation)
                .attr("x", textX)
                .text(lines[i]);
           }});

function wordwrap2( str, width, brk, cut ) {
    brk = brk || '\n';
    width = width || 75;
    cut = cut || false;
    if (!str) { return str; }
    var regex = '.{0,' +width+ '}(\\s|$)' + (cut ? '|.{' +width+ '}|.+$' : '|\\S+?(\\s|$)');
    return str.match( RegExp(regex, 'g') ).join( brk );
};

person Derek Smith    schedule 01.12.2016    source источник


Ответы (1)


Выберите метку и переместите ее вверх на половину высоты, используя lines.length и separation:

d3.select(this).attr("transform", "translate(0," + (separation*lines.length/2*-1) + ")");

Вот демо:

<script src="https://d3js.org/d3.v2.min.js?2.9.3"></script>
<style>
    .link {
        stroke: #aaa;
    }
    .node text {
        stroke: #333;
        cursor: pointer;
    }
    .node circle {
        stroke: #fff;
        stroke-width: 3px;
        fill: #555;
    }
</style>

<body>
    <script>
        var width = 400,
            height = 300

        var svg = d3.select("body").append("svg")
            .attr("width", width)
            .attr("height", height);

        var force = d3.layout.force()
            .distance(50)
            .charge(-3000)
            .size([width, height]);

        var json = {
            "nodes": [{
                "name": "node1"
            }, {
                "name": "node2"
            }, {
                "name": "node3"
            }, {
                "name": "node4"
            }, {
                "name": "node5 has a very very very very long long name"
            }],
            "links": [{
                "source": 0,
                "target": 1
            }, {
                "source": 0,
                "target": 2
            }, {
                "source": 0,
                "target": 3
            }, {
                "source": 0,
                "target": 4
            }]
        };

        force
            .nodes(json.nodes)
            .links(json.links)
            .start();

        var link = svg.selectAll(".link")
            .data(json.links)
            .enter().append("line")
            .attr("class", "link")
            .style("stroke-width", 2);

        var node = svg.selectAll(".node")
            .data(json.nodes)
            .enter().append("g")
            .attr("class", "node")
            .call(force.drag);

        node.append("circle")
            .attr("r", 8);
						
						var maxLength = 10;
var separation = 12;
var textX = 0;

        node.append("text")
          .attr("dominant-baseline", "central")
            .attr("dx", 12)
            .attr("dy", ".35em")
            .each(function (d) {
          var lines = wordwrap2(d.name, maxLength).split('\n');
          for (var i = 0; i < lines.length; i++) {
              d3.select(this)
                .append("tspan")
                .attr("dy", separation)
                .attr("x", textX)
                .text(lines[i]);
								
								d3.select(this).attr("transform", "translate(0," + (separation*lines.length/2*-1) + ")");
           }});
						
						
						function wordwrap2( str, width, brk, cut ) {
    brk = brk || '\n';
    width = width || 75;
    cut = cut || false;
    if (!str) { return str; }
    var regex = '.{0,' +width+ '}(\\s|$)' + (cut ? '|.{' +width+ '}|.+$' : '|\\S+?(\\s|$)');
    return str.match( RegExp(regex, 'g') ).join( brk );
};

        force.on("tick", function() {
            link.attr("x1", function(d) {
                    return d.source.x;
                })
                .attr("y1", function(d) {
                    return d.source.y;
                })
                .attr("x2", function(d) {
                    return d.target.x;
                })
                .attr("y2", function(d) {
                    return d.target.y;
                });

            node.attr("transform", function(d) {
                return "translate(" + d.x + "," + d.y + ")";
            });
        });
    </script>

person Gerardo Furtado    schedule 02.12.2016
comment
К сожалению, это не работает для моего графика. Я считаю, что это связано с тем, как я присваивал текстовые метки узлам. Я отредактировал исходный код, включив в него узел var. Почему бы преобразованию не скорректировать положение метки на моей диаграмме, поскольку метки добавляются к узлам? - person Derek Smith; 02.12.2016
comment
Боюсь, чтобы ответить на этот вопрос, вам придется создать рабочую скрипку или плункер. - person Gerardo Furtado; 02.12.2016
comment
Я считаю, что это должно работать: jsfiddle.net/dereksmith5822/eyu9tw37 - person Derek Smith; 02.12.2016
comment
Чтобы это работало, вам нужно добавить в группу и круги, и тексты. Проверьте обновленную скрипту: jsfiddle.net/gerardofurtado/ynpj48f7 - person Gerardo Furtado; 02.12.2016
comment
Спасибо за предоставление этой обновленной версии. К сожалению, я потратил довольно много времени на тестирование, но мне все еще трудно изменить свой сценарий, чтобы сделать его функциональным. У меня есть другие функции, зависящие от текущей структуры, и если я изменю текстовую переменную для добавления к узлу, как в вашем примере, график выйдет из строя. Я все еще учусь, но есть ли способ добавить текст в круг без изменения .enter().append('text') или .enter().append('circle') в моем jsFiddle? - person Derek Smith; 03.12.2016
comment
Нет, вы не можете добавить текст в круг. - person Gerardo Furtado; 03.12.2016