Как заставить силы тяжести пузырьков в D3. js - PullRequest
0 голосов
/ 15 февраля 2020

В приведенном ниже коде мне нужно, чтобы пузыри с наивысшими значениями плавали в левой части экрана, но я не имею глубоких знаний о D3. js, и я не могу найти способ сделать this.

Мой код

<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>

Редактировать в JS Fiddle

Код дает такой результат

Мне нужно, чтобы пузыри с самыми высокими значениями были слева, как на рисунке ниже.

Какое свойство или функцию D3. js можно использовать для контроля над уровнем пузырьков, когда они мне нужны? Спасибо!

1 Ответ

1 голос
/ 18 февраля 2020

Для этого нет функции специально в d3.pack. d3.force позволяет вам указывать позиции x и y на основе значения данных и может достигать искомого результата.

Симуляция силы имеет функции .x и .y, которые основаны на количестве данных , а затем, чтобы избежать наложений, функция .collide корректирует положение окружностей в зависимости от их радиуса (плюс небольшое отступ в 3 пикселя).

 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>
...