Search code examples
extjsd3.jstooltipbubble-chart

Tooltip positioning in bubble chart


I have created a d3.js bubble chart. I want the tooltip to be positioned always at the top right point on the border of the circle, wherever the cursor is moved inside the circle.

This is how I created the tooltip.

        new Ext.ToolTip({
        title: desc,
        radius:circle[0][i].__data__.radius,
        anchor: 'left',
        autoHide: false,
        showDelay:0,
        trackMouse: false,
        anchorToTarget: false,
        ttype:"ltype"+i,
        target: circle[0][i],
        listeners:{
             move:function(a,b,c){
                  var currentType = this.ttype;
                 items.push(this);
                 for(var i=0;i<items.length;i++){
                     if(items[i].ttype!=currentType){
                         if(items[i].isVisible()){
                           items[i].hide();   
                         }
                     }
                 }

                }
        }

    });

How can I do it? Here is my fiddle


Solution

  • There is a possible solution if you are using mouse over events.

    var data = {
      name: "layout",
      children: [{
        name: "AxisLayout",
        size: 6725
      }, {
        name: "BundledEdgeRouter",
        size: 3727
      }, {
        name: "CircleLayout",
        size: 9317
      }, {
        name: "CirclePackingLayout",
        "size": 12003
      }, {
        name: "DendrogramLayout",
        "size": 4853
      }, {
        name: "ForceDirectedLayout",
        "size": 8411
      }, {
        name: "IcicleTreeLayout",
        "size": 4864
      }, {
        name: "IndentedTreeLayout",
        "size": 3174
      }, {
        name: "Layout",
        "size": 7881
      }, {
        name: "NodeLinkTreeLayout",
        "size": 12870
      }, {
        name: "PieLayout",
        "size": 2728
      }, {
        name: "RadialTreeLayout",
        "size": 12348
      }, {
        name: "RandomLayout",
        "size": 870
      }, {
        name: "StackedAreaLayout",
        "size": 9121
      }, {
        name: "TreeMapLayout",
        "size": 9191
      }]
    };
    
    var margin = {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
      },
      width = 400 - margin.left - margin.right,
      height = 400 - margin.top - margin.bottom;
    
    var n = data.children.length,
      m = 1,
      padding = 6,
      radius = d3.scale.sqrt().range([0, 12]),
      color = d3.scale.category10().domain(d3.range(m)),
      x = d3.scale.ordinal().domain(d3.range(m)).rangePoints([0, width], 1);
    
    var nodes = d3.range(n).map(function() {
      var i = Math.floor(Math.random() * m), //color
        v = (i + 1) / m * -Math.log(Math.random()); //value
      return {
        radius: radius(v),
        color: color(i),
        cx: x(i),
        cy: height / 2,
      };
    
    });
    
    nodes.forEach(function(item, index) {
      item.radius = data.children[index].size / 300;
    });
    
    var force = d3.layout.force()
      .nodes(nodes)
      .size([width, height])
      .gravity(0)
      .charge(0)
      .on("tick", tick)
      .start();
    
    var drag = force.drag()
      .on("drag", dragmove);
    
    function dragmove(d) {
      var euclideanDistance = Math.sqrt(Math.pow((d.px - 198), 2) + Math.pow((d.py - 198), 2));
    
      if (euclideanDistance > 198 - d.radius) {
        d.px = d.px - 198;
        d.py = d.py - 198;
    
        var radians = Math.atan2(d.py, d.px);
    
        d.px = Math.cos(radians) * (198 - d.radius) + 198;
        d.py = Math.sin(radians) * (198 - d.radius) + 198;
      }
    }
    
    var svg = d3.select("#chart").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    var circle = svg.selectAll("circle")
      .data(nodes)
      .enter().append("circle")
      .attr("r", function(d) {
        return d.radius;
      })
      .style("fill", function(d, i) {
        return "green";
      })
      .call(drag);
    
    var tip = Ext.create('Ext.tip.ToolTip', {
      title: 'test',
      width: 150,
      height: 40,
      radius: 5,
      hidden: true,
      anchor: 'left',
      autoHide: false,
      trackMouse: true,
      anchorToTarget: false
    });
    
    circle.on('mouseover', function(d, i) {
      tip.setTitle("radius: " + d.radius);
      tip.showAt([d.x - d.radius, d.y - d.radius]);
    });
    
    function tick(e) {
      circle.each(gravity(.2 * e.alpha))
        .each(collide(.5))
        .attr("cx", function(d) {
          return d.x;
        })
        .attr("cy", function(d) {
          return d.y;
        });
    }
    
    // Move nodes toward cluster focus.
    function gravity(alpha) {
      return function(d) {
        d.y += (d.cy - d.y) * alpha;
        d.x += (d.cx - d.x) * alpha;
      };
    }
    
    // Resolve collisions between nodes.
    function collide(alpha) {
      var quadtree = d3.geom.quadtree(nodes);
      return function(d) {
        var r = d.radius + radius.domain()[1] + padding,
          nx1 = d.x - r,
          nx2 = d.x + r,
          ny1 = d.y - r,
          ny2 = d.y + r;
        quadtree.visit(function(quad, x1, y1, x2, y2) {
          if (quad.point && (quad.point !== d)) {
            var x = d.x - quad.point.x,
              y = d.y - quad.point.y,
              l = Math.sqrt(x * x + y * y),
              r = d.radius + quad.point.radius + (d.color !== quad.point.color) * padding;
            if (l < r) {
              l = (l - r) / l * alpha;
              d.x -= x *= l;
              d.y -= y *= l;
              quad.point.x += x;
              quad.point.y += y;
            }
          }
          return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
        });
      };
    }
    svg {
      /*border: 1px solid black;*/
      border-radius: 50%;
      background: lightgray;
    }
    circle {
      stroke: #fff;
    } 
    <link href="http://dev.sencha.com/deploy/ext-4.1.0-gpl/resources/css/ext-all.css" rel="stylesheet"/>
    <script src="http://cdn.sencha.com/ext/gpl/4.1.1/ext-all.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    <p id='chart'>