如何使用日志比例显示使用d3 JS显示正确的堆积条形图?

时间:2016-01-18 11:46:18

标签: d3.js

我正在尝试使用d3 js显示stak图表。我也尝试使用对数刻度,但没有开始工作。

8月份赌注栏未正确显示,或者可能会有更多此类案例。

如何使用对数比例正确显示?

var margins = {
top: 12,
left: 48,
right: 24,
bottom: 24
},
legendPanel = {
width: 180
},
width = 500 - margins.left - margins.right - legendPanel.width,
height = 100 - margins.top - margins.bottom,
dataset = [{
    data: [{
        month: 'Aug',
        count: 1
    }, {
        month: 'feb',
        count: 234
    }, {
        month: 'mar',
        count: 345
    }],
    name: 'Series #1'
}, {
    data: [{
        month: 'Aug',
        count: 3
    }, {
        month: 'feb',
        count: 267
    }, {
        month: 'mar',
        count: 573
    }],
    name: 'Series #2'
},
{
    data: [{
        month: 'Aug',
        count: 20
    }, {
        month: 'feb',
        count: 267
    }, {
        month: 'mar',
        count: 573
    }],
    name: 'Series #3'
}

],
series = dataset.map(function (d) {
    return d.name;
}),
dataset = dataset.map(function (d) {
    return d.data.map(function (o, i) {
        // Structure it so that your numeric
        // axis (the stacked amount) is y
        return {
            y: o.count,
            x: o.month
        };
    });
}),
stack = d3.layout.stack();

stack(dataset);

var dataset = dataset.map(function (group) {
return group.map(function (d) {
    // Invert the x and y values, and y0 becomes x0
    return {
        x: d.y,
        y: d.x,
        x0: d.y0
    };
});
}),
  svg = d3.select('body')
    .append('svg')
    .attr('width', width + margins.left + margins.right + legendPanel.width)
    .attr('height', height + margins.top + margins.bottom)
    .append('g')
    .attr('transform', 'translate(' + margins.left + ',' + margins.top + ')'),
xMax = d3.max(dataset, function (group) {
    return d3.max(group, function (d) {
        return d.x + d.x0;
    });
}),
xScale = d3.scale.linear()
    .domain([0, xMax])
    .range([0, width]),
months = dataset[0].map(function (d) {
    return d.y;
}),
_ = console.log(months),
yScale = d3.scale.ordinal()
    .domain(months)
    .rangeRoundBands([0, height], .1),
xAxis = d3.svg.axis()
    .scale(xScale)
    .orient('bottom'),
yAxis = d3.svg.axis()
    .scale(yScale)
    .orient('left'),
colours = d3.scale.category10(),
groups = svg.selectAll('g')
    .data(dataset)
    .enter()
    .append('g')
    .style('fill', function (d, i) {
    return colours(i);
}),
rects = groups.selectAll('rect')
    .data(function (d) {
    return d;
})
    .enter()
    .append('rect')
    .attr('x', function (d) {
    return xScale(d.x0);
})
    .attr('y', function (d, i) {
    return yScale(d.y);
})
    .attr('height', function (d) {
    return yScale.rangeBand();
})
    .attr('width', function (d) {
    return xScale(d.x);
})
    .on('mouseover', function (d) {
    var xPos = parseFloat(d3.select(this).attr('x')) / 2 + width / 2;
    var yPos = parseFloat(d3.select(this).attr('y')) + yScale.rangeBand() / 2;

    d3.select('#tooltip')
        .style('left', xPos + 'px')
        .style('top', yPos + 'px')
        .select('#value')
        .text(d.x);

    d3.select('#tooltip').classed('hidden', false);
})
    .on('mouseout', function () {
    d3.select('#tooltip').classed('hidden', true);
})

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

 svg.append('g')
.attr('class', 'axis')
.call(yAxis);

svg.append('rect')
.attr('fill', 'yellow')
.attr('width', 160)
.attr('height', 30 * dataset.length)
.attr('x', width + margins.left)
.attr('y', 0);

series.forEach(function (s, i) {
svg.append('text')
    .attr('fill', 'black')
    .attr('x', width + margins.left + 8)
    .attr('y', i * 24 + 24)
    .text(s);
svg.append('rect')
    .attr('fill', colours(i))
    .attr('width', 60)
    .attr('height', 20)
    .attr('x', width + margins.left + 90)
    .attr('y', i * 24 + 6);
});

示例代码jsfiddle

1 个答案:

答案 0 :(得分:0)

d3中有内置的对数刻度,但还需要进行一些修改。 您需要做的第一件事是将xScale更改为对数:

xScale = d3.scale.log()
        .domain([0.5, xMax])//The x-min can not be 0!
        .range([0, width]),

注意xmin是0.5而不是0.因为0不能以对数标度存在。放置大于1的任何东西都会使得系列#1的8月不可见,因为它是1。

您需要的第二件事是以不同的方式计算宽度,因为您的比例不再是线性的,即10-> 20宽于110-> 120:

.attr('width', function (d) {
        return xScale(d.x0+d.x)- (d.x0==0 ? 0 : xScale(d.x0));
    })

这只是取结束点(d.x0+d.x)的位置并减去起点d.x0的位置。不是如果d.x0 == 0,则0用作起始位置,因为0在xScale中不存在

最后,默认情况下会将刻度显示为1e+02,因此请更改刻度线的格式

xAxis = d3.svg.axis()
    .scale(xScale)
    .orient('bottom')
    .ticks(0, '.1s'),

请参阅jsfiddle