Масштабирование стрелки на маркере ссылки компоновки силы D3

У меня есть следующий код в графе, направленном силой d3, где я пытаюсь изменить размер ссылок и связанных с ними наконечников стрел на основе значения (от 1-3). Вес хода изменяется со значением, но наконечники стрел не остаются в правильном положении. Он имеет тенденцию смещаться назад от конца, когда вес хода изменяется от 1 до 3. Есть идеи о том, как правильно выровнять стрелки (маркеры) при изменении значения хода? Большое спасибо!

      var link = vis.selectAll("line.link")
      .data(json.links)
    .enter().append("svg:line")
      .attr("class", "link")
      .style("stroke-width", function(d) { return Math.sqrt(d.value); })
      .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; })
      .attr("marker-end", "url(#arrowGray)")
      .on("click", function(d) {
            link.style("stroke","#dddddd");
            node.style("stroke","#FFFFFF");
            d3.select(this).style("stroke","red");
            link.attr("marker-end", null);
            link.attr("marker-end", "url(#arrowGray)");
            d3.select(this).attr("marker-end", null);
            d3.select(this).attr("marker-end", "url(#arrowRed)");
            clickLink(d);
            });

    defs.append("svg:marker")
            .attr("id", "arrowGray")
            .attr("viewBox","0 0 10 10")
            .attr("refX","20")
            .attr("refY","5")
            .attr("markerUnits","strokeWidth")
            .attr("markerWidth","9")
            .attr("markerHeight","5")
            .attr("orient","auto")
            .append("svg:path")
            .attr("d","M 0 0 L 10 5 L 0 10 z")
            .attr("fill", "#BBBBBB");

2 ответов


Проблема

этой jsFiddle демонстрирует проблему в вопрос. Значения маркера def относятся к ширине стока элемента, к которому он прикреплен (линии связи в данном случае). Вижу markerUnits spec. Используя strokeWidth на markerUnits атрибут координаты для стрелок разного размера будут немного отличаться. Короче говоря, соответствующие значения для одного размера стрелки не будут правильно переводить на другой размеры.

Решение 1: Несколько Маркеров

как уже упоминалось в комментариях, одним из решений было бы создать другой маркер для каждого strokeWidth что нужно. Это сработало бы, только если бы вы знали заранее, какие размеры необходимы.

решение 2: Измените строки

другой вариант-изменить конечную точку для линий. Вместо того, чтобы позволить линиям завершиться в центре узла, завершите его на внешнем краю узла. Этот jsFiddle демонстрирует это. Это облегчает необходимость смещения стрелки, так как теперь мы можем просто нарисовать ее в конце линии.

это решение включает в себя некоторые математику, чтобы выяснить, что x2 и y2 значения line должно быть. Таким образом, он не может быть идеальным для систем с большим числом ребер.

var nodeRadius = 10;
var lineX2 = function (d) {
    var length = Math.sqrt(Math.pow(d.target.y - d.source.y, 2) + Math.pow(d.target.x - d.source.x, 2));
    var scale = (length - nodeRadius) / length;
    var offset = (d.target.x - d.source.x) - (d.target.x - d.source.x) * scale;
    return d.target.x - offset;
};
var lineY2 = function (d) {
    var length = Math.sqrt(Math.pow(d.target.y - d.source.y, 2) + Math.pow(d.target.x - d.source.x, 2));
    var scale = (length - nodeRadius) / length;
    var offset = (d.target.y - d.source.y) - (d.target.y - d.source.y) * scale;
    return d.target.y - offset;
};

var link = svg.selectAll("line.link")
    .data(graph.links)
    .enter().append("svg:line")
    .attr("class", "link")
    .style("stroke-width", function (d) {
      return Math.sqrt(d.value);
    })
    .attr("x1", function (d) {
       return d.source.x;
    })
    .attr("y1", function (d) {
        return d.source.y;
    })
    .attr("x2", lineX2)
    .attr("y2", lineY2)
    .attr("marker-end", "url(#arrowGray)")
    .on("click", function (d) {
        link.style("stroke", "#dddddd");
        node.style("stroke", "#FFFFFF");
        d3.select(this).style("stroke", "red");
        link.attr("marker-end", null);
        link.attr("marker-end", "url(#arrowGray)");
        d3.select(this).attr("marker-end", null);
        d3.select(this).attr("marker-end", "url(#arrowRed)");
    });

var defs = svg.append('defs');
defs.append("svg:marker")
    .attr("id", "arrowGray")
    .attr("viewBox", "0 0 10 10")
    .attr("refX", "10")
    .attr("refY", "5")
    .attr("markerUnits", "strokeWidth")
    .attr("markerWidth", "10")
    .attr("markerHeight", "5")
    .attr("orient", "auto")
    .append("svg:path")
    .attr("d", "M 0 0 L 10 5 L 0 10 z")
    .attr("fill", "#000");

var node = svg.selectAll(".node")
    .data(graph.nodes)
    .enter().append("circle")
    .attr("class", "node")
    .attr("r", nodeRadius)
    .style("fill", function (d) {
        return color(d.group);
    })
    .call(force.drag);

node.append("title")
    .text(function (d) {
        return d.name;
    });

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

    node.attr("cx", function (d) {
        return d.x;
    })
    .attr("cy", function (d) {
        return d.y;
    });
});

код refX - Это, наверное, слишком большие. Попробуйте поставить что-то около 1.