简体   繁体   English

使用d3.js中的tickValues控制栏位置

[英]Controlling bar position with tickValues in d3.js

I'm trying to build a multi series bar chart using d3 but running into problems due to the sparse nature of the dataset. 我正在尝试使用d3构建多系列条形图,但由于数据集的稀疏性质而遇到问题。

I want to force the x-axis to have a tick for every day, even if there is no data. 我想强制x轴每天都打勾,即使没有数据也是如此。 The test data I have can have data points that are weeks apart so I'm expecting wide areas with no bars - which is fine. 我拥有的测试数据可以具有相隔数周的数据点,因此我期望没有条形的广阔区域-很好。

I thought I could force the xAxis to use a set of predefined ticks using the tickValues array, but these leads to very strange display of overlaying the text for each day on top of days that do have some data. 我以为我可以通过tickValues数组强制xAxis使用一组预定义的刻度,但是这会导致非常奇怪的显示,即在包含某些数据的日子上每天叠加文本。

I've included a screenshot of what I mean. 我提供了我的意思的屏幕截图。 xAxis标签文字覆盖

I get the feeling I'm supposed to do something when calculating the width of the bars but can't figure out what that might be. 我觉得在计算条的宽度时应该做些什么,但无法弄清楚那是什么。

Code: 码:

var data = [];
var tickValues = [];

var max = _.max(chartData.tabular, function(assessment) { return assessment.dateUTC; });
var min = _.min(chartData.tabular, function(assessment) { return assessment.dateUTC; });
var iter = moment.twix(min.dateUTC, max.dateUTC).iterate("days");

while(iter.hasNext()){

    var momentObj = iter.next();

    var assessment = _.find(chartData.tabular, {'date': momentObj.format('DD/MM/YYYY')});

    tickValues.push(momentObj.valueOf());

    if(assessment != null){

        if(assessment.type == 'calculated'){
            data.push({date: momentObj.valueOf(), calculated: assessment.score, manual: null});
        }

        if(assessment.type == 'manual'){
            data.push({date: momentObj.valueOf(), calculated: null, manual: assessment.score});
        }
    }
}

log(data);

var margin = {top: 20, right: 55, bottom: 30, left: 40},
    width  = $('#cahai-chart').width() - margin.left - margin.right,
    height = 500  - margin.top  - margin.bottom;

var x = d3.scale.ordinal()
    .rangeRoundBands([0, width], .1);

var y = d3.scale.linear()
    .rangeRound([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickValues(tickValues)
    .tickFormat(function(d){return d3.time.format('%d/%m/%y')(new Date(d))});

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var color = d3.scale.ordinal()
    .range(["#001c9c","#101b4d","#475003","#9c8305","#d3c47c"]);

var svg = d3.select("#cahai-chart svg")
    .attr("width",  width  + margin.left + margin.right)
    .attr("height", height + margin.top  + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var labelVar = 'date';
var varNames = d3.keys(data[0]).filter(function (key) { return key !== labelVar;});
color.domain(varNames);

data.forEach(function (d) {
    var y0 = 0;
    d.mapping = varNames.map(function (name) {
        return {
            name: name,
            label: d[labelVar],
            y0: y0,
            y1: y0 += +d[name]
        };
    });
    d.total = d.mapping[d.mapping.length - 1].y1;
});

x.domain(data.map(function (d) { return d.date; }));
y.domain([0, d3.max(data, function (d) { return d.total; })]);

svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

svg.append("g")
    .attr("class", "y axis")
    .call(yAxis)
    .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Score");

var selection = svg.selectAll(".series")
    .data(data)
    .enter().append("g")
    .attr("class", "series")
    .attr("transform", function (d) { return "translate(" + x(d.date) + ",0)"; });

selection.selectAll("rect")
    .data(function (d) { return d.mapping; })
    .enter().append("rect")
    .attr("width", x.rangeBand())
    .attr("y", function (d) { return y(d.y1); })
    .attr("height", function (d) { return y(d.y0) - y(d.y1); })
    .style("fill", function (d) { return color(d.name); })
    .style("stroke", "grey")
    .on("mouseover", function (d) { showPopover.call(this, d); })
    .on("mouseout",  function (d) { removePopovers(); })

var legend = svg.selectAll(".legend")
    .data(varNames.slice().reverse())
    .enter().append("g")
    .attr("class", "legend")
    .attr("transform", function (d, i) { return "translate(55," + i * 20 + ")"; });

legend.append("rect")
    .attr("x", width - 10)
    .attr("width", 10)
    .attr("height", 10)
    .style("fill", color)
    .style("stroke", "grey");

legend.append("text")
    .attr("x", width - 12)
    .attr("y", 6)
    .attr("dy", ".35em")
    .style("text-anchor", "end")
    .text(function (d) { return d; });

function removePopovers () {
    $('.popover').each(function() {
        $(this).remove();
    });
}

function showPopover (d) {
    $(this).popover({
        title: d.name,
        placement: 'auto top',
        container: 'body',
        trigger: 'manual',
        html : true,
        content: function() {
            return "Date: " + d3.time.format('%d/%m/%y')(new Date(d.label)) +
                "<br/>Score: " + d3.format(",")(d.value ? d.value: d.y1 - d.y0); }
    });
    $(this).popover('show')
}

An ordinal scale will always show as many ticks are there are values in the domain. 序数刻度将始终显示域中存在值的数量。 You just need to pass the full array of dates as the domain. 您只需要传递完整的日期数组作为域。

Replace this line 替换此行

x.domain(data.map(function (d) { return d.date; }));

with this 有了这个

x.domain(tickValues);

It looks like you have everything else set up correctly, so this will space the bars out along the axis and make them slimmer. 看来您已正确设置了所有其他项,因此这将使条沿轴间隔开并使它们更细。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM