Textpath in circle packing not zooming correctly

问题: I am using d3.js version 5 and circle-pack and d3 arc generator. Circle pack and zoom code is standard and used at many places. When I try to put arc text along circle, it...

问题:

I am using d3.js version 5 and circle-pack and d3 arc generator. Circle pack and zoom code is standard and used at many places. When I try to put arc text along circle, it works fine and it loads fine but when I try to zoom in or zoom out , text/labels does not accordingly put along with circle border. Here is my code: https://codepen.io/dmd7/pen/BvMwbr

I tried 'd3-circle-text' plug-in as mentioned here in 'd3-circle-text' on zoomable circle-pack but did not work as it is using older version of d3 plus zoom in functionality is different there.

Also, wanted to try what is achieved here: http://nbremer.github.io/occupations/ but did not work as again converting from older d3 version to newer d3 version makes it harder.

Here is my code: 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>

and here is javaScript code:

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

Appreciate your help.

Thank You.


回答1:

You're not selecting the paths for the <textPath>s in the zoom function. It can be done as this:

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

Of course, a better idea is naming your selections, and also improving the new d attribute. Also, mind creating globals (like circle and node), just for using them in the zoom function. Finally, your logic for setting the display property of the textpaths is not working correctly.

Here is the forked Codepen: https://codepen.io/anon/pen/QzYawj?editors=0010

  • 发表于 2019-01-16 05:27
  • 阅读 ( 323 )
  • 分类:网络文章

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除