首页 > 解决方案 > d3.js 气泡分布以匹配容器尺寸

问题描述

我想创建一个动态.container,其中每个都circle保留其size比率并始终分布以适应.container.

如何修改代码以使数据点的分布始终调整到边界的大小,.container同时保持size每个的比率circle

.container {
  width: 400px;
  height: 200px;
}

(function() {
  var json = {
    call_data: [
      [
        "Lifestyle",
        1,
        "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/5bb3ce2f801fbc657f83dd57_pp-lifestyle(white).svg"
      ],
      [
        "Sports",
        2,
        "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/5c9131911ad86f445cb5abc7_pp-sport(white).svg"
      ],
      [
        "Environment",
        8,
        "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/59f2a4bef42fff000159ba7a_pp-environ(white).svg"
      ],
      [
        "Medical",
        6,
        "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/59f2a4dc831e8500015fda53_pp-health(white).svg"
      ],
      [
        "Food",
        4,
        "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/59f8c2cc78cc2d0001fd4a7e_pp-food(white).svg"
      ]
    ]
  };
  var width = 200;
  var height = 200;

  var tooltip = d3
    .select(".bubble_chart")
    .append("div")
    .classed("tooltip", true);

  var svg = d3
    .select(".bubble_chart")
    .append("svg")
    //responsive SVG needs these 2 attributes and no width and height attr
    .attr("preserveAspectRatio", "xMinYMin meet")
    .attr("viewBox", "0 0 " + width + " " + height);

  var bubble = d3.layout
    .pack()
    .size([200, 200])
    .value(function(d) {
      return d.size;
    })
    .padding(12);

  // generate data with calculated layout values
  var nodes = bubble.nodes(processData(json)).filter(function(d) {
    return !d.children;
  }); // filter out the outer bubble

  var vis = svg.selectAll("circle").data(nodes, function(d, i) {
    return d.name + i;
  });

  vis
    .enter()
    .append("circle")
    .attr("transform", function(d) {
      return "translate(" + d.x + "," + d.y + ")";
    })
    .attr("class", function(d) {
      return d.className;
    })
    .attr("r", 0)
    .transition()
    .duration(1000)
    .attr("r", function(d) {
      return d.r;
    });

  vis
    .enter()
    .append("svg:image")
    .attr("transform", d => "translate(" + d.x + "," + d.y + ")")
    .attr("x", d => -(d.r / 1.5) / 2)
    .attr("y", d => -(d.r / 1.5) / 2)
    .attr("xlink:href", function(d) {
      return d.img;
    })
    .attr("width", d => d.r / 1.5)
    .transition()
    .duration(1000)
    .style("opacity", 1);

  /*
  vis
    .enter()
    .append("text")
    .attr("transform", d => "translate(" + d.x + "," + d.y + ")")
    .attr("fill", "white")
    .attr("text-anchor", "middle")
    .attr("font-size", d => d.r / (d.r * 5 / 100))
    .text(d => d.name);
*/
  /*  vis
    .enter()
    .append("text")
    .attr("transform", d => "translate(" + d.x + "," + d.y + ")")
    .attr("fill", "white")
    .attr("text-anchor", "middle")
    .attr("font-size", d => d.r / (d.r * 3 / 100))
    .text(d => d.value);
    */
  vis
    .on("mousemove", function(d) {
      tooltip
        .style("opacity", 1)
        .style("left", d3.event.x - tooltip.node().offsetWidth / 2 + "px")
        .style("top", d3.event.y + 25 + "px").html(`
                <p>Category: ${d.name}</p>
                <p>Ordered: ${d.value}</p>
           
             `);
    })
    .on("mouseout", function() {
      tooltip.style("opacity", 0);
    });

  function processData(data) {
    var obj = data.call_data;

    var newDataSet = [];

    for (var prop in obj) {
      newDataSet.push({
        name: obj[prop][0],
        className: obj[prop][0].toLowerCase(),
        size: obj[prop][1],
        img: obj[prop][2]
      });
    }
    return {
      children: newDataSet
    };
  }

  var aspect = width / height,
    chart = d3.select('.bubble_chart');
  d3.select(window)
    .on("resize", function() {
      var targetWidth = chart.node().getBoundingClientRect().width;
      chart.attr("width", targetWidth);
      chart.attr("height", targetWidth / aspect);
    });
})();
.container {
  border: 2px solid red;
  width: 400px;
  height: 200px;
}

.bubble_chart {
  flex: 1;
  border: 2px solid
}

.lifestyle {
  fill: #89BED3;
}

.sports {
  fill: #2A83D4;
}

.environment {
  fill: #6CC070;
}

.food {
  fill: #665C9E;
}

.medical {
  fill: #C13E40;
}

.tooltip {
  opacity: 0;
  position: absolute;
  pointer-events: none;
  background-color: #fafafa;
  border-radius: 8px;
  padding: 15px;
  z-index: 999;
  box-shadow: 1px 1px 3px 0 rgba(0, 0, 0, .1)
}

.tooltip p {
  margin: 0;
}

.tooltip:before {
  content: " ";
  position: absolute;
  border: 12px solid transparent;
  border-bottom-color: #fafafa;
  top: -20px;
  left: 50%;
  margin-left: -12px;
}
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.js"></script>
<div class="container">
  <div class="bubble_chart"></div>
</div>

标签: javascriptjqueryd3.js

解决方案


您应该在一开始就将宽度和高度设置为边界矩形高度和宽度。现在您将其设置为 200px,它们是相同的。

这样做:

var width = document.querySelector(".container").getBoundingClientRect().width;  
var height = document.querySelector(".container").getBoundingClientRect().height;

(function() {
  var json = {
    call_data: [
      [
        "Lifestyle",
        1,
        "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/5bb3ce2f801fbc657f83dd57_pp-lifestyle(white).svg"
      ],
      [
        "Sports",
        2,
        "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/5c9131911ad86f445cb5abc7_pp-sport(white).svg"
      ],
      [
        "Environment",
        8,
        "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/59f2a4bef42fff000159ba7a_pp-environ(white).svg"
      ],
      [
        "Medical",
        6,
        "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/59f2a4dc831e8500015fda53_pp-health(white).svg"
      ],
      [
        "Food",
        4,
        "https://uploads-ssl.webflow.com/59df9e77ad9420000140eafe/59f8c2cc78cc2d0001fd4a7e_pp-food(white).svg"
      ]
    ]
  };
  var width = document.querySelector(".container").getBoundingClientRect().width;  
  var height = document.querySelector(".container").getBoundingClientRect().height;

  var tooltip = d3
    .select(".bubble_chart")
    .append("div")
    .classed("tooltip", true);

  var svg = d3
    .select(".bubble_chart")
    .append("svg")
    //responsive SVG needs these 2 attributes and no width and height attr
    .attr("preserveAspectRatio", "xMinYMin meet")
    .attr("viewBox", "0 0 " + width + " " + height);

  var bubble = d3.layout
    .pack()
    .size([200, 200])
    .value(function(d) {
      return d.size;
    })
    .padding(12);

  // generate data with calculated layout values
  var nodes = bubble.nodes(processData(json)).filter(function(d) {
    return !d.children;
  }); // filter out the outer bubble

  var vis = svg.selectAll("circle").data(nodes, function(d, i) {
    return d.name + i;
  });

  vis
    .enter()
    .append("circle")
    .attr("transform", function(d) {
      return "translate(" + d.x + "," + d.y + ")";
    })
    .attr("class", function(d) {
      return d.className;
    })
    .attr("r", 0)
    .transition()
    .duration(1000)
    .attr("r", function(d) {
      return d.r;
    });

  vis
    .enter()
    .append("svg:image")
    .attr("transform", d => "translate(" + d.x + "," + d.y + ")")
    .attr("x", d => -(d.r / 1.5) / 2)
    .attr("y", d => -(d.r / 1.5) / 2)
    .attr("xlink:href", function(d) {
      return d.img;
    })
    .attr("width", d => d.r / 1.5)
    .transition()
    .duration(1000)
    .style("opacity", 1);

  /*
  vis
    .enter()
    .append("text")
    .attr("transform", d => "translate(" + d.x + "," + d.y + ")")
    .attr("fill", "white")
    .attr("text-anchor", "middle")
    .attr("font-size", d => d.r / (d.r * 5 / 100))
    .text(d => d.name);
*/
  /*  vis
    .enter()
    .append("text")
    .attr("transform", d => "translate(" + d.x + "," + d.y + ")")
    .attr("fill", "white")
    .attr("text-anchor", "middle")
    .attr("font-size", d => d.r / (d.r * 3 / 100))
    .text(d => d.value);
    */
  vis
    .on("mousemove", function(d) {
      tooltip
        .style("opacity", 1)
        .style("left", d3.event.x - tooltip.node().offsetWidth / 2 + "px")
        .style("top", d3.event.y + 25 + "px").html(`
                <p>Category: ${d.name}</p>
                <p>Ordered: ${d.value}</p>
           
             `);
    })
    .on("mouseout", function() {
      tooltip.style("opacity", 0);
    });

  function processData(data) {
    var obj = data.call_data;

    var newDataSet = [];

    for (var prop in obj) {
      newDataSet.push({
        name: obj[prop][0],
        className: obj[prop][0].toLowerCase(),
        size: obj[prop][1],
        img: obj[prop][2]
      });
    }
    return {
      children: newDataSet
    };
  }

  var aspect = width / height,
    chart = d3.select('.bubble_chart');
  d3.select(window)
    .on("resize", function() {
      var targetWidth = chart.node().getBoundingClientRect().width;
      chart.attr("width", targetWidth);
      chart.attr("height", targetWidth / aspect);
    });
})();
.container {
  border: 2px solid red;
  width: 400px;
  height: 200px;
}

.bubble_chart {
  flex: 1;
  border: 2px solid
}

.lifestyle {
  fill: #89BED3;
}

.sports {
  fill: #2A83D4;
}

.environment {
  fill: #6CC070;
}

.food {
  fill: #665C9E;
}

.medical {
  fill: #C13E40;
}

.tooltip {
  opacity: 0;
  position: absolute;
  pointer-events: none;
  background-color: #fafafa;
  border-radius: 8px;
  padding: 15px;
  z-index: 999;
  box-shadow: 1px 1px 3px 0 rgba(0, 0, 0, .1)
}

.tooltip p {
  margin: 0;
}

.tooltip:before {
  content: " ";
  position: absolute;
  border: 12px solid transparent;
  border-bottom-color: #fafafa;
  top: -20px;
  left: 50%;
  margin-left: -12px;
}
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.js"></script>
<div class="container">
  <div class="bubble_chart"></div>
</div>


推荐阅读