Search code examples
javascriptchartsc3

How to create scale breaks in c3 charts


I have to represent a data in C3 bar charts. The problem is the dataset usually contains one or two very large values with the other much smaller values(smaller in comparison). This results in very tall bars next to bars that are almost invisible. I know using scale breaks like this can solve the problem. But i have not found a single example in C3 that even mentions it. Is there any way to create the scale breaks in C3 charts? If not, are there any alternative open source JS libraries to do it?


Solution

  • You could transform the values and the scale using a d3 linear scale with split domains.

    And then draw the split using multiple grid lines

    HTML

    <div id="chart"></div>
    

    Script

    var myScale = d3.scale.linear().domain([0, 10, 90, 100]).range([0, 40, 60, 100]);
    
    var chart = c3.generate({
        data: {
            columns: [
                ['A', 3, 2, 1, 4, 1.5, 92.5, 93, 91, 94].map(function (value, index) {
                    return index ? myScale(value) : value;
                })],
            type: 'bar'
        },
        tooltip: {
            format: {
                value: function (value) {
                    return parseFloat(myScale.invert(value).toFixed(5));
                }
            }
        },
        axis: {
            y: {
                min: 0,
                max: 100,
                padding: {
                    top: 0,
                    bottom: 0
                },
                tick: {
                    format: function (d) {
                        if (d > myScale.range()[1] && d < myScale.range()[2]) return null
                        else return myScale.invert(d)
                    }
                }
            }
        },
        grid: {
            y: {
                lines: [{ value: (myScale.range()[1] + myScale.range()[2]) / 2, class: 'breakFill' }, 
                        {value: (myScale.range()[1] + myScale.range()[2]) / 2 + 1, class: 'break' }, 
                        {value: (myScale.range()[1] + myScale.range()[2]) / 2 - 1, class: 'break' }]
            }
        }
    });
    

    You have to use grid lines and not regions because the region layer is below the bars layer (the grid layer is above the bar layer)

    CSS

    This consists of a (thick) fill line and 2 border lines

    .breakFill > line {
        stroke-width: 4px;
        stroke: white;
        stroke-opacity: 1;
    }
    .break > line {
        stroke-width: 1px;
        stroke: grey;
    }
    

    You could also style it better using a squiggly pattern or position an HTML element with a squiggly background over it.

    Fiddle - http://jsfiddle.net/tdfpj3oc/