Заставить пузырьки влево в D3. js - PullRequest
5 голосов
/ 07 апреля 2020

Я пытаюсь разными способами создать пузырьковую диаграмму, форсируя левую часть страницы в зависимости от значения. Наибольшее значение пузыря должно быть в верхнем левом углу. Поскольку другие подходы не сработали, я искал пример, в котором пузырьки перемещаются в центр страницы, и теперь я пытаюсь отрегулировать влево, результат, который мне нужен, такой же, как на изображении выше.

enter image description here

Мой код:

function bubbleChart() {

var width = 940;
var height = 600;

var center = {
    x: width / 2,
    y: height / 2
};
var left = {
    x: 0,
    y: 300
};

var forceStrength = 0.03;

var svg = null;
var bubbles = null;
var nodes = [];

function charge(d) {
    return -Math.pow(d.radius, 2.0) * forceStrength;
}

var simulation = d3.forceSimulation()
    .velocityDecay(0.2)
    .force('x', d3.forceX().strength(forceStrength).x(left.x))
    .force('y', d3.forceY().strength(forceStrength).y(left.y))
    .force('charge', d3.forceManyBody().strength(charge))
    .on('tick', ticked);

simulation.stop();

var fillColor = d3.scaleOrdinal()
    .domain(['low', 'medium', 'high'])
    .range(['#d84b2a', '#beccae', '#7aa25c']);

function createNodes(rawData) {

    var maxAmount = d3.max(rawData, function(d) {
        return +d.total_amount;
    });

    var radiusScale = d3.scalePow()
        .exponent(0.5)
        .range([2, 85])
        .domain([0, maxAmount]);

    var myNodes = rawData.map(function(d) {
        return {
            id: d.id,
            radius: radiusScale(+d.total_amount),
            value: +d.total_amount,
            name: d.grant_title,
            org: d.organization,
            group: d.group,
            year: d.start_year,
            x: Math.random() * 900,
            y: Math.random() * 800
        };
    });

    myNodes.sort(function(a, b) {
        return b.value - a.value;
    });

    return myNodes;
}

var chart = function chart(selector, rawData) {

    nodes = createNodes(rawData);

    svg = d3.select(selector)
        .append('svg')
        .attr('width', width)
        .attr('height', height);

    bubbles = svg.selectAll('.bubble')
        .data(nodes, function(d) {
            return d.id;
        });

    var bubblesE = bubbles.enter().append('circle')
        .classed('bubble', true)
        .attr('r', 0)
        .attr('fill', function(d) {
            return fillColor(d.group);
        })
        .attr('stroke', function(d) {
            return d3.rgb(fillColor(d.group)).darker();
        })
        .attr('stroke-width', 2)
        .on('mouseover', showDetail)
        .on('mouseout', hideDetail);

    bubbles = bubbles.merge(bubblesE);

    bubbles.transition()
        .duration(2000)
        .attr('r', function(d) {
            return d.radius;
        });

    simulation.nodes(nodes);

    groupBubbles();
};

function ticked() {
    bubbles
        .attr('cx', function(d) {
            return d.x;
        })
        .attr('cy', function(d) {
            return d.y;
        });
}

function nodeYearPos(d) {
    return yearCenters[d.year].x;
}

function groupBubbles() {
    hideYearTitles();

    // @v4 Reset the 'x' force to draw the bubbles to the center.
    simulation.force('x', d3.forceX().strength(forceStrength).x(center.x));

    // @v4 We can reset the alpha value and restart the simulation
    simulation.alpha(1).restart();
}

function splitBubbles() {
    showYearTitles();

    // @v4 Reset the 'x' force to draw the bubbles to their year centers
    simulation.force('x', d3.forceX().strength(forceStrength).x(nodeYearPos));

    // @v4 We can reset the alpha value and restart the simulation
    simulation.alpha(1).restart();
}

function hideYearTitles() {
    svg.selectAll('.year').remove();
}

function showYearTitles() {
    // Another way to do this would be to create
    // the year texts once and then just hide them.
    var yearsData = d3.keys(yearsTitleX);
    var years = svg.selectAll('.year')
        .data(yearsData);

    years.enter().append('text')
        .attr('class', 'year')
        .attr('x', function(d) {
            return yearsTitleX[d];
        })
        .attr('y', 40)
        .attr('text-anchor', 'middle')
        .text(function(d) {
            return d;
        });
}

function showDetail(d) {
    // change outline to indicate hover state.
    d3.select(this).attr('stroke', 'black');

    var content = '';

    tooltip.showTooltip(content, d3.event);
}

function hideDetail(d) {
    // reset outline
    d3.select(this)
        .attr('stroke', d3.rgb(fillColor(d.group)).darker());

    tooltip.hideTooltip();
}

Это мой полный код на Вижубе: https://vizhub.com/barbosa-renan/2864204410d54af5a5c402cdfdd9959d?edit=files&file=index.js

Примечание: в других попытках я пытался использовать .domain () и .range, но в этом сценарии это не сработало, потому что, когда у меня есть пузырьки с очень высоким значением и другие с очень низкое значение, они очень далеки.

...