How to force the gravity of bubbles in D3.js

纵然是瞬间 提交于 2021-01-29 09:19:17

问题


In the code below, I need to make the bubbles with the highest values ​​float to the left of the screen, but I have no deep knowledge of D3.js and I can't find a way to do this.

My code

<script type="text/javascript">

     dataset = {
        "children": [{"Name":"Olives","Count":10},
            {"Name":"Tea","Count":8},
            {"Name":"Mashed Potatoes","Count":6},
            {"Name":"Boiled Potatoes","Count":5},
            {"Name":"Milk","Count":4},
            {"Name":"Chicken Salad","Count":4},
            {"Name":"Vanilla Ice Cream","Count":2},
            {"Name":"Cocoa","Count":7}];

    var diameter = 600;
    var color = d3.scaleOrdinal(d3.schemeCategory20);

    var bubble = d3.pack(dataset)
        .size([diameter, diameter])
        .padding(1.5);

    var svg = d3.select("body")
        .append("svg")
        .attr("width", diameter)
        .attr("height", diameter)
        .attr("class", "bubble");

    var nodes = d3.hierarchy(dataset)
        .sum(function(d) { return d.Count; });

    var node = svg.selectAll(".node")
        .data(bubble(nodes).descendants())
        .enter()
        .filter(function(d){
            return  !d.children
        })
        .append("g")
        .attr("class", "node")
        .attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
        });

    node.append("title")
        .text(function(d) {
            return d.Name + ": " + d.Count;
        });

    node.append("circle")
        .attr("r", function(d) {
            return d.r;
        })
        .style("fill", function(d,i) {
            return color(i);
        });

    node.append("text")
        .attr("dy", ".2em")
        .style("text-anchor", "middle")
        .text(function(d) {
            return d.data.Name.substring(0, d.r / 3);
        })
        .attr("font-family", "sans-serif")
        .attr("font-size", function(d){
            return d.r/5;
        })
        .attr("fill", "white");

    node.append("text")
        .attr("dy", "1.3em")
        .style("text-anchor", "middle")
        .text(function(d) {
            return d.data.Count;
        })
        .attr("font-family",  "Gill Sans", "Gill Sans MT")
        .attr("font-size", function(d){
            return d.r/5;
        })
        .attr("fill", "white");

    d3.select(self.frameElement)
        .style("height", diameter + "px");

</script>

Edit in JS Fiddle

The code produces this result

I need the bubbles with the highest values ​​to be on the left, as in the image below.

What property or function of D3.js can I use to control the severity of the bubbles as I need them? Thanks!


回答1:


There's not a function specifically in d3.pack for this. d3.force allows you specify x and y positions based on the data's value, and could achieve the result you are looking for.

The force simulation has an .x and .y function that is based on the data's count, and then to avoid overlaps, the .collide function adjusts the circles positions based on their radius (plus a small paddding of 3px).

 var simulation = d3.forceSimulation(nodes)
        .force("forceX", d3.forceX().strength(.051).x(d => xScale(d.Count)))
        .force("forceY", d3.forceY().strength(.051).y(d => yScale(d.Count)))
        .force('collision', d3.forceCollide().radius(d => rScale(d.Count) + 3))

dataset = {
        "children": [{"Name":"Olives","Count":10},
            {"Name":"Tea","Count":8},
            {"Name":"Mashed Potatoes","Count":6},
            {"Name":"Boiled Potatoes","Count":5},
            {"Name":"Milk","Count":4},
            {"Name":"Chicken Salad","Count":4},
            {"Name":"Vanilla Ice Cream","Count":2},
            {"Name":"Cocoa","Count":7}]
    }
    
    let nodes = dataset.children

    var width = 600;
    var height = 600;
    var margin = 50
    var color = d3.scaleOrdinal(d3.schemeCategory20);
    
    let extentCount = d3.extent(nodes, d => d.Count)
    let maxRadius = 100 
    
    let yScale = d3.scaleLinear()
    	.domain(extentCount)
    	.range([height - maxRadius, maxRadius])
    
    let xScale = d3.scaleLinear()
    	.domain(extentCount)
    	.range([(width - maxRadius), maxRadius])
    
    let rScale = d3.scaleSqrt()
    	.domain(extentCount)
    	.range([5, maxRadius])
    
    var svg = d3.select("body")
        .append("svg")
        .attr("width", width + margin + margin)
        .attr("height", height + margin + margin)
        .attr("class", "bubble");
    
    var g = svg.append("g")
    	.attr("transform", "translate(" + margin + "," + margin + ")")
    
    var simulation = d3.forceSimulation(nodes)
        .force("forceX", d3.forceX().strength(.051).x(d => xScale(d.Count)))
        .force("forceY", d3.forceY().strength(.051).y(d => yScale(d.Count)))
        .force('collision', d3.forceCollide().radius(d => rScale(d.Count) + 3))
    		.on("tick", function(d){
            node
                .attr("cx", function(d){ return d.x; })
                .attr("cy", function(d){ return d.y; })
          })
    		.stop()
    
    for (var i = 0; i < 120; i++) {
      simulation.tick()
    }

    var node = g.selectAll(".node")
        .data(nodes)
        .enter()
        .append("g")
        .attr("class", "node")
        .attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
        });

    node.append("title")
        .text(function(d) {
            return d.Name + ": " + d.Count;
        });

    node.append("circle")
        .attr("r", d => rScale(d.Count))
        .style("fill", function(d,i) {
            return color(i);
        });

    node.append("text")
        .attr("dy", ".2em")
        .style("text-anchor", "middle")
        .text(function(d) {
            return d.Name.substring(0, rScale(d.Count) / 3);
        })
        .attr("font-family", "sans-serif")
        .attr("font-size", function(d){
            return rScale(d.Count)/5;
        })
        .attr("fill", "white");

    node.append("text")
        .attr("dy", "1.3em")
        .style("text-anchor", "middle")
        .text(function(d) {
            return d.Count;
        })
        .attr("font-family",  "Gill Sans", "Gill Sans MT")
        .attr("font-size", function(d){
            return d.r/5;
        })
        .attr("fill", "white");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>


来源:https://stackoverflow.com/questions/60240368/how-to-force-the-gravity-of-bubbles-in-d3-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!