圆形包装中的文本路径无法正确缩放

时间:2019-01-15 00:54:18

标签: javascript d3.js svg zooming circle-pack

我正在使用d3.js版本5以及circle-pack和d3弧生成器。 Circle pack和缩放代码是标准的,并在许多地方使用。当我尝试沿圆弧放置弧形文本时,它可以正常工作并且可以很好地加载,但是当我尝试放大或缩小时,文本/标签并没有相应地与圆弧边界一起放置。这是我的代码:https://codepen.io/dmd7/pen/BvMwbr

我尝试过'd3-circle-text' on zoomable circle-pack中在此处提到的“ d3-circle-text”插件,但由于它使用的是较早版本的d3,并且放大功能有所不同,因此无法正常工作。

此外,我想尝试一下这里实现的目标:http://nbremer.github.io/occupations/,但由于再次从较旧的d3版本转换为较新的d3版本而使工作变得更加困难,因此无法正常工作。

这是我的代码:https://codepen.io/dmd7/pen/BvMwbr

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>test</title>
        <script src="https://d3js.org/d3.v5.min.js"></script>
        <style>
            text {
            font-size: 11px;
            }
            text.parent {
            fill: #1f77b4;
            }
            text {
            font: 10px sans-serif;
            }
            /*.text {
              cursor: pointer;
            }*/
            text:hover {
            /*color: #FF0000; */
            font-weight: bold;
            stroke: #000;
            stroke-width: 0.5px;
            text-decoration: underline;
            }
            circle {
                fill: #ccc;
                stroke: #999;
                pointer-events: all;
                cursor: pointer;
                opacity: 0.55;
            }
            circle.parent {
                fill: #1f77b4;
                fill-opacity: .1;
                stroke: steelblue;
            }
            circle.parent:hover {
                stroke: #ff7f0e;
                stroke-width: .5px;
            }
            /*circle.child {
                pointer-events: none;
            }*/
            /*Node hover*/
             /*.node {
                cursor: pointer;
            }
            .node:hover {
                stroke: #000;
                stroke-width: 1.5px;
            }
            .node--leaf {
                fill: white;
            }
            */

            /*Circle text*/
        .label {
            font: 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
            text-anchor: middle;
            text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff, 0 -1px 0 #fff;
        }

        .label,
        .node--root,
        .node--leaf {
            pointer-events: none;
        }
        .arc-path {
            visibility: hidden;
        }
        </style>
    </head>
    <body>
        <div id="container">
            <svg width="700" height="700" id="cir_svg"></svg>
        </div>
    </body>
</html>

这是javaScript代码:

var svg, margin, diameter, g, pack, focus, view, circle, node;
var maxNumLevels = 3;

document.addEventListener("DOMContentLoaded", function (e) {

    svg = d3.select("svg"),
        margin = 20,
        diameter = +svg.attr("width");
        g = svg.append("g").attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");

    pack = d3.pack()
        .size([diameter - margin, diameter - margin])
        .padding(2);

    initialSpecialCase();
});

function generateLevels(pivotLevel) {
    pivNum = parseInt(pivotLevel.match(/VirusL(\d+)_s/)[1]);
    var r  = '';
  for (var n = 0; n <= maxNumLevels; ++n) {
    if(n!=0) {
        r += ","; 
    }
    r += "VirusL" + (pivNum + n) + "_s";
    }
    curLevelsStr = r;
  return r;
}

function extractTaxIdFromName(nameTaxId) {
    var idx = nameTaxId.lastIndexOf(", taxid:");
    if(idx != -1) {
        return nameTaxId.substring(idx + 8);
    }
    return '';
}

function initialSpecialCase() {
    var data1 = {};
    d3.json("https://api.myjson.com/bins/1h5co8").then(function(dta) {
        console.info("Data from ajax: ", dta);
        data1 = {
            'name'  : 'root',
            'field' : '',
        'value' : dta.response.numFound,
        'pivot' : dta['facet_counts']['facet_pivot'][generateLevels('VirusL0_s')]
        };
        createCircles(data1);
    });
}

function mapData(d) {
    if (!d.hasOwnProperty('pivot'))
      return;

  d.hasChildren = true;

  var maxPivName = "VirusL" + (pivNum + maxNumLevels - 1).toString() + "_s";
  if (d.field === maxPivName)
      return;

  var cnodes = d.pivot;
  for (c in cnodes) {
      if (!cnodes[c].hasOwnProperty('name')) {
          cnodes[c].name = cnodes[c].value;
          cnodes[c].value = cnodes[c].count;
      }
  }
  return cnodes;
}

function arcSVG(mx0, my0, r, larc, sweep, mx1, my1) {
  return 'M'+mx0+','+my0+' A'+r+','+r+' 0 '+larc+','+sweep+' '+mx1+','+my1;
}

function createCircles(data) {

    var root = d3.hierarchy(data, mapData)
        .sort(function(a, b) { return b.value - a.value; });
    console.info(root);

    focus = root;
  var nodes = pack(root).descendants();

  var nodeg = g.selectAll(".node")
        .data(nodes)
        .enter().append("g")
        .attr("class", function(d) {
            return d.children ? "node" : "leaf node"; 
        })
       ;

  nodeg.append("circle")
    .attr("class", function(d) { return d.children ? "parent" : "child"; })
    .attr("id", function(d) { return 'c' + extractTaxIdFromName(d.data.name); })
    .on("click", function(d) { 
      selectedVirus = d.data.name;
      if (!d.children && d.parent) {
        zoom(d); d3.event.stopPropagation();
      }
      else if (focus !== d) { 
        zoom(d); 
        d3.event.stopPropagation(); 
      } 
    });

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

    nodeg.each(function(d, i) {
        var gg = d3.select(this);
        if(d.depth === 3) {
//          gg.append('text')
//                  .style('font-size', d3.min([3 * d.r / d.data.name.length, 16]))
//                  .attr('dy', '0.3em')
//                  .text(d.data.name);
        }
        else if(d.depth > 0) {
                var rr = d.r - 5;
                gg.append('path')           
            .attr('d', arcSVG(-rr, 0, rr, 1, 1, rr, 0))
            .attr('id', 'label-path-' + i)
            .style('fill', 'none')
            .style('stroke', 'none');

           gg.append('text')
            .append('textPath')
            .attr('xlink:href', '#label-path-' + i)
            .attr('startOffset', '50%')
            .style("text-anchor","middle")
            .style('font-size', '10px')
            .style('fill', 'black')
            .text(d.data.name);
            }
    });


  node = nodeg.selectAll("circle,text");
  circle = nodeg.selectAll("circle");

  svg
      .on("click", function() {
            zoom(root);
      });

  zoomTo([root.x, root.y, root.r * 2 + margin]);

}

function zoom(d) {
  var focus0 = focus; focus = d;

  var transition = d3.transition()
      .duration(d3.event.altKey ? 7500 : 750)
      .tween("zoom", function(d) {
        var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
        return function(t) { zoomTo(i(t)); };
      });

  transition.selectAll("text")
    .filter(function(d) { return d.parent === focus || this.style.display === "inline"; })
      .style("fill-opacity", function(d) { return d.parent === focus ? 1 : 0; })
      .on("start", function(d) { if (d.parent === focus) this.style.display = "inline"; })
      .on("end", function(d) { if (d.parent !== focus) this.style.display = "none"; });
}

function zoomTo(v) {
  var k = diameter / v[2]; view = v;
  node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
  circle.attr("r", function(d) { return d.r * k; });
}

感谢您的帮助。

谢谢。

1 个答案:

答案 0 :(得分:2)

您没有在缩放功能中选择<textPath>的路径。可以这样完成:

svg.selectAll("path")
    .attr('d', function(d){
        return arcSVG(-(d.r - 5) * k, 0, (d.r - 5) * k, 1, 1, (d.r - 5) * k, 0)
    });

当然,一个更好的主意是命名您的选择,并改进新的d属性。另外,请记住创建全局变量(例如circlenode),只是为了在缩放功能中使用它们。最后,您用于设置文本路径的display属性的逻辑无法正常工作。

以下是分叉的Codepen:https://codepen.io/anon/pen/QzYawj?editors=0010