Создание двойной гистограммы - PullRequest
0 голосов
/ 13 мая 2018

Я хочу создать гистограмму следующим образом:

enter image description here

Есть два столбца графика один под другим, первый растет вверх, а второй - вниз. У них разные масштабы и данные.

Это то, что я создал:

var doublebarSvg1 = d3.select('#doublebar')
    .append('svg')
    .attr('class', 'doublebarSvg1')
    .attr('width', 700)
    .attr('height', 400);

var doublebarSvg2 = d3.select('#doublebar')
    .append('svg')
    .attr('class', 'doublebarSvg2')
    .attr('width', 700)
    .attr('height', 400);

var margin = {top: 0, right: 0, bottom: 0, left: 50};

var width = doublebarSvg1.attr('width') - margin.left - margin.right;
var height = doublebarSvg1.attr('height') - margin.top - margin.bottom;

var x = d3.scaleBand()
    .rangeRound([0, width])
    .padding(0.1)
    .domain(years);

var y1 = d3.scaleLinear()
    .rangeRound([height, 0])
    .domain([0, 100]);

var y2 = d3.scaleSqrt()
    .rangeRound([height, 0])
    .domain([813, 0.1]); // max value 812.05 but domain is [0, 100000]

var doublebarSvgG1 = doublebarSvg1.append('g').attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');
var doublebarSvgG2 = doublebarSvg2.append('g').attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

////////////////////////////////////////////////////////////////////////
// Tooltip.
////////////////////////////////////////////////////////////////////////
var svgTip = doublebarSvg1.append('svg').attr('id', 'tooltip');

var tip = d3.tip()
    .attr('class', 'd3-tip')
    .offset([-5, 0])
    .html(function(d) {
        return '<div><span>Country:</span> <span style=\'color:white\'>' + d.country + '</span></div>' +
                 '<div><span>Perc:</span> <span style=\'color:white\'>' + d.perc + '%</span></div>' +
                 '<div><span>Rate:</span> <span style=\'color:white\'>' + d.rate + '%</span></div>';
    });
svgTip.call(tip);

////////////////////////////////////////////////////////////////////////
// Draw a single double bar
////////////////////////////////////////////////////////////////////////
makeDoublebar1();

function makeDoublebar1() {
    // define the axes
    var xAxis = d3.axisBottom(x); 
    var yAxis1 = d3.axisLeft(y1);

    // create x axis
    doublebarSvgG1.append('g')
        .attr('class', 'x axis')
        .attr('transform', 'translate(0, ' + height + ')') 
        .call(xAxis)
        .selectAll('text')  
        .style('text-anchor', 'end')
        .attr('dx', '-.8em')
        .attr('dy', '.15em')
        .attr('transform', 'rotate(-65)');

    // create y axis
    doublebarSvgG1.append('g')
        .attr('class', 'y axis')
        .call(yAxis1)
        .append('text')
        .attr('transform', 'rotate(-90)')
        .attr('y', 6)
        .attr('dy', '.71em')
        .style('text-anchor', 'end');

    // create bar rect
    doublebarSvgG1.selectAll('.bar')
        .data(testData1) //.data(covFiltered)
        .enter().append('rect')
        .attr('fill', 'steelblue')
        .attr('class', 'bar')
        .attr('x', function(d) {
            return x(d.year); 
        })
        .attr('y', function(d) { 
            if(isNaN(d.perc)) {
                d.perc = 0;
            }
            return y1(d.perc); 
        })
        .attr('width', x.bandwidth())
        .attr('height', function(d) { 
            if(isNaN(d.perc)) {
                d.perc = 0;
            }
            return height - y1(d.perc); 
        })
        .on('mouseover', function(d) {
            d3.select(this).attr('fill', 'darkblue');
            tip.show(d);
        })
        .on('mouseout', function(d) {
            d3.select(this).attr('fill', 'steelblue');
            tip.hide(d);
        });
}

////////////////////////////////////////////////////////////////////////
// Draw a single double bar
////////////////////////////////////////////////////////////////////////
makeDoublebar2();

function makeDoublebar2() {
    // define the axes
    var xAxis = d3.axisBottom(x); 
    var yAxis2 = d3.axisLeft(y2);

    // create x axis
    doublebarSvgG2.append('g')
        .attr('class', 'x axis')
        .attr('transform', 'translate(0, 0)') 
        .call(xAxis)
        .selectAll('text')  
        .style('text-anchor', 'end')
        .attr('dx', '-.8em')
        .attr('dy', '.15em')
        .attr('transform', 'rotate(-65)');

    // create y axis
    doublebarSvgG2.append('g')
        .attr('class', 'y axis')
        .call(yAxis2)
        .append('text')
        .style('text-anchor', 'end');

    // create bar rect
    doublebarSvgG2.selectAll('.bar')
        .data(testData2)
        .enter().append('rect')
        .attr('fill', 'tomato')
        .attr('class', 'bar')
        .attr('x', function(d) { // left start point
            return x(d.year); 
        })
        .attr('y', function(d) { // top start point
            if(isNaN(d.rate)) {
                d.rate = 0;
            }
            return 0; 
        })
        .attr('width', x.bandwidth())
        .attr('height', function(d) { 
            if(isNaN(d.rate)) {
                d.perc = 0;
            }
            return y2(d.rate); 
        })
        .on('mouseover', function(d) {
            d3.select(this).attr('fill', 'red');
            tip.show(d);
        })
        .on('mouseout', function(d) {
            d3.select(this).attr('fill', 'tomato');
            tip.hide(d);
        });
}

PLUNKER здесь .

Есть некоторые проблемы:

  1. если я заменим .axis {display: initial;} на .axis {display: none;}, все оси исчезнут, но я хочу горизонтальную линию между двумя диаграммами

  2. Мне бы хотелось, чтобы была только одна подсказка, которая, когда пользователь наводит курсор на любую полосу, выходит с подсказкой, которая показывает значения perc и rate.

И, что более важно, это самый умный способ создать такую ​​диаграмму?

1 Ответ

0 голосов
/ 13 мая 2018

Что касается оси, так как вы хотите сохранить горизонтальную линию, просто скройте галочки и тексты:

.x.axis text,.x.axis line {
  opacity: 0;
}

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

Поэтому лучше всего найти нужный объект в каждом массиве при наведении курсора на определенный год и получить соответствующие свойства.:

var thisPerc = testData1.find(function(e){return e.year === d.year}).perc;
var thisRate = testData2.find(function(e){return e.year === d.year}).rate;

Затем вы используете эти свойства для установки текста всплывающей подсказки.

Вот обновленный Plunker: http://plnkr.co/edit/tfB4TpkETgzp5GF1677p?p=preview

Наконец, для вашего последнего вопроса ( «И, что более важно, это самый умный способ создать такую ​​диаграмму?» ), ответ нет .Здесь есть много вещей, которые можно (и нужно) изменить, но это требует большого рефакторинга, и это, возможно, не по теме в переполнении стека.Однако это адекватный вопрос для Code Review .Но, пожалуйста, сначала прочитайте их документы , чтобы оставить свой вопрос по теме, спрашивать здесь не то же самое, что спрашивать здесь.

...