D3,工具提示位置,应用viewBox时

时间:2016-11-20 19:07:03

标签: d3.js svg responsive-design tooltip viewbox

我正在创建饼图。

当悬停细分时,我想显示自定义html工具提示...

从一开始,我一直在使用d3.event.pageXd3.event.pageY来计算工具提示位置。

但现在我想在饼图段的中心点显示工具提示

enter image description here

所以,我正在计算悬停时的质心

 var centroid = arcs.pie.centroid(d);
            var left = centroid[0];
            var top = centroid[1];

并相应地指定工具提示的左,右位置

tooltip.style("left", (left) + "px")
       .style("top", (top) + "px");

仅当图表大小为初始值时才有效,但我也希望图表具有响应性

所以,我在svg上分配了viewbox属性

 var svg = d3.select(selector)
        .append('svg')
        .attr("viewBox", "0 0 " + attrs.svgWidth + " " + attrs.svgHeight)
        .attr("preserveAspectRatio", "xMidYMid meet")

问题出现了:

调整窗口大小时,图表会相应缩放,但工具提示位置超出分段中心。

如何将此工具提示移至正确的位置?

我正在寻找“无插件”解决方案。

结帐codepen

1 个答案:

答案 0 :(得分:1)

这里需要夫妻计算。首先,在获得质心后,您需要取消它,因为它们处于g转换(并且您的div需要定向到顶部/左侧)。然后你可以简单地得到svg的当前/实际宽度/高度,并按照它与初始非观察框宽度/高度的比例来缩放位置。

var centroid = arcs.pie.centroid(d),
    svgDim = svg.node().getBoundingClientRect();

var left = (centroid[0] + dynamic.marginLeft) * (svgDim.width/attrs.svgWidth),
    top = (centroid[1] + dynamic.marginTop) * (svgDim.height/attrs.svgHeight);

这是完整运行的代码:

function packData(p) {
  var data = p;
  var result = {
    title: data.datasets[0].label,
    data: []
  };

  data.labels.forEach(function(v) {
    result.data.push({
      label: v
    });
  });

  result.data.forEach(function(v, i) {
    v.value = data.datasets[0].data[i];

    if (data.datasets[0].backgroundColor) {
      v.backgroundColor = data.datasets[0].backgroundColor[i];
    }

    if (data.extras) {
      for (var attrname in data.extras[i]) {
        v[attrname] = data.extras[i][attrname];
      }
    }
  });

  return result;

}

var svg;

function drawD3JsPie(selector) {

  var info = packData({
    labels: ["Niger", "Cameroon", "Georgia", "Spain", "United States", "Singapore", "Qatar"],
    datasets: [{
      label: 'GDP   Per Capita in $ (2015)',
      data: [1080, 3144, 9630, 34819, 55805, 85253, 132099],
      backgroundColor: ["#FF6384", "#36A2EB", "#FFCE56", "#AA6384", "#2CA21B", "#678E86", "#FFAE86"]
    }]
  });
  var data = info.data;
  // ########   hard coded and dynamically calculated attributes  #######

  var attrs = {
    svgWidth: 600,
    svgHeight: 600,
    marginLeft: 4,
    marginBottom: 20,
    marginRight: 4,
    marginTop: 20,
    textColor: '#7f7777',
    fontSize: '13px',
    pieStroke: 'white',
    pieStrokeWidth: 3,
    titleText: info.title,
    hoverColorImpact: 1,
    animationDuration: 1200,
    animationEase: 'out',
    titleHeight: 30,
    tooltipTextColor: 'white',
    tooltipBackgroundColor: 'black',

  }

  var dynamic = {}
  dynamic.chartWidth = attrs.svgWidth - attrs.marginLeft - attrs.marginRight
  dynamic.chartHeight = attrs.svgHeight - attrs.marginTop - attrs.marginBottom - attrs.titleHeight;
  dynamic.pieOuterRadius = Math.min(dynamic.chartWidth, dynamic.chartHeight) / 2;
  dynamic.chartTopMargin = attrs.marginTop + attrs.titleHeight;
  dynamic.marginLeft = attrs.marginLeft + dynamic.pieOuterRadius;
  dynamic.marginTop = dynamic.chartTopMargin + dynamic.pieOuterRadius;

  //  ##############     SCALES     #########


  //  ##############   ARCS   ###############
  var arcs = {}
  arcs.pie = d3.arc()
    .outerRadius(dynamic.pieOuterRadius - 10)
    .innerRadius(0);


  //  ##########     layouts  #######
  var layouts = {};
  layouts.pie = d3.pie()
    .sort(null)
    .value(function(d) {
      return d.value;
    });


  //###############  STARTUP ANIMATIONS ###############
  var tweens = {}

  tweens.pieIn = function(endData) {
    var startData = {
      startAngle: 0,
      endAngle: 0
    };
    var interpolation = d3.interpolate(startData, endData);
    return function(currentData) {
      return arcs.pie(interpolation(currentData));
    }
  };


  //  ########### RESPONSIVE SVG DRAWING  ##############
  svg = d3.select(selector)
    .append('svg')
    .attr("viewBox", "0 0 " + attrs.svgWidth + " " + attrs.svgHeight)
    .attr("preserveAspectRatio", "xMidYMid meet")



  //   #################  CHART CONTENT DRAWING  ###############

  var chart = svg.append('g')
    .attr('width', dynamic.chartWidth)
    .attr('height', dynamic.chartHeight)
    .attr('transform', 'translate(' + (dynamic.marginLeft) + ',' + (dynamic.marginTop) + ')');


  var pieArcs = chart.selectAll('.arc')
    .data(layouts.pie(data))
    .enter()
    .append("g")
    .attr("class", "arc");


  pieArcs.append("path")
    //.attr("d", arcs.pie)
    .attr('stroke-width', attrs.pieStrokeWidth)
    .attr('stroke', attrs.pieStroke)
    .attr("fill", function(d) {
      return d.data.backgroundColor;
    })
    .transition()
    .duration(1000)
    .attrTween("d", tweens.pieIn);






  //  ################   ADDING TOOLTIP ##################
  var div = d3.select(selector)
    .append("div")
    .attr("class", "tooltip")
    .style("opacity", 0)
    .style("position", 'absolute')
    .style("text-align", 'left')
    .style("font", '12px sans-serif')
    .style("background", attrs.tooltipBackgroundColor)
    .style("padding", '5px')
    .style("color", attrs.tooltipTextColor)
    .style("border", '0px')
    .style("border-radius", '4px')
    .style("pointer-events", 'none')

  d3.selectAll('.arc')
    .on("mouseover", function(d, i) {
      var centroid = arcs.pie.centroid(d),
        svgDim = svg.node().getBoundingClientRect();

      var left = (centroid[0] + dynamic.marginLeft) * (svgDim.width / attrs.svgWidth),
        top = (centroid[1] + dynamic.marginTop) * (svgDim.height / attrs.svgHeight);

      d3.select(this)
        .append('circle')
        .style('fill', 'steelblue')
        .attr("transform", "translate(" + centroid + ")")
        .attr("r", 10);

      var buffer = d.value.toString().length;
      if (i > data.length / 2) {
        buffer = -buffer - 180;
      } else {
        buffer *= 4;
      }
      div.transition()
        .duration(100)
        .style("opacity", .9);

      div.html("<b>" + attrs.titleText + "</b><br/>" + d.data.label + ' : ' + d.data.value)
        .style("left", (left) + "px")
        .style("top", (top) + "px");

      var currPath = d3.select(this).select('path');
      var darkenedColor = d3.rgb(currPath.attr('fill')).darker(attrs.hoverColorImpact);
      currPath.attr('fill', darkenedColor);


    })
    .on("mouseout", function(d) {


      div.transition()
        .duration(200)
        .style("opacity", 0);

      var currPath = d3.select(this).select('path');
      var changedColor = d3.rgb(currPath.attr('fill')).darker(-attrs.hoverColorImpact);
      currPath.attr('fill', changedColor);

    });




}

drawD3JsPie("#d3JsPie");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"></script>
<div style='height:50%;width:50%'id="d3JsPie"></div>