Chart JS Fill Between two lines
Asked Answered
C

3

11

I am looking for a way to fill between two lines with Chart.js so that it would look like this. I have looked and everything seems to talk about filling between two lines across zero. I also need other lines to fill all the way down like normal. Is this something chart.js can do?

Fill between 2 lines

California answered 4/1, 2017 at 15:50 Comment(0)
B
18

Here is a solution that uses a plugin to fill between two datasets. Supports all line styles and fill shading between multiple lines. To fill between a dataset, use the custom param fillBetweenSet to tell a dataset to fill the area between another dataset.

Fiddle - https://jsfiddle.net/ke5n5LnL/26/

Preview:

enter image description here

Code:

<html>
    <div>
        <canvas id="demo"></canvas>
    </div>
</html>

<script>
    var fillBetweenLinesPlugin = {
    afterDatasetsDraw: function (chart) {
        var ctx = chart.chart.ctx;
        var xaxis = chart.scales['x-axis-0'];
        var yaxis = chart.scales['y-axis-0'];
        var datasets = chart.data.datasets;
        ctx.save();

        for (var d = 0; d < datasets.length; d++) {
            var dataset = datasets[d];
            if (dataset.fillBetweenSet == undefined) {
                continue;
            }

            // get meta for both data sets
            var meta1 = chart.getDatasetMeta(d);
            var meta2 = chart.getDatasetMeta(dataset.fillBetweenSet);

            // do not draw fill if one of the datasets is hidden
            if (meta1.hidden || meta2.hidden) continue;

            // create fill areas in pairs
            for (var p = 0; p < meta1.data.length-1;p++) {
                // if null skip
              if (dataset.data[p] == null || dataset.data[p+1] == null) continue;

              ctx.beginPath();

              // trace line 1
              var curr = meta1.data[p];
              var next = meta1.data[p+1];
              ctx.moveTo(curr._view.x, curr._view.y);
              ctx.lineTo(curr._view.x, curr._view.y);
              if (curr._view.steppedLine === true) {
                ctx.lineTo(next._view.x, curr._view.y);
                ctx.lineTo(next._view.x, next._view.y);
              }
              else if (next._view.tension === 0) {
                ctx.lineTo(next._view.x, next._view.y);
              }
              else {
                  ctx.bezierCurveTo(
                    curr._view.controlPointNextX,
                    curr._view.controlPointNextY,
                    next._view.controlPointPreviousX,
                    next._view.controlPointPreviousY,
                    next._view.x,
                    next._view.y
                  );
                            }

              // connect dataset1 to dataset2
              var curr = meta2.data[p+1];
              var next = meta2.data[p];
              ctx.lineTo(curr._view.x, curr._view.y);

              // trace BACKWORDS set2 to complete the box
              if (curr._view.steppedLine === true) {
                ctx.lineTo(curr._view.x, next._view.y);
                ctx.lineTo(next._view.x, next._view.y);
              }
              else if (next._view.tension === 0) {
                ctx.lineTo(next._view.x, next._view.y);
              }
              else {
                // reverse bezier
                ctx.bezierCurveTo(
                  curr._view.controlPointPreviousX,
                  curr._view.controlPointPreviousY,
                  next._view.controlPointNextX,
                  next._view.controlPointNextY,
                  next._view.x,
                  next._view.y
                );
              }

                            // close the loop and fill with shading
              ctx.closePath();
              ctx.fillStyle = dataset.fillBetweenColor || "rgba(0,0,0,0.1)";
              ctx.fill();
            } // end for p loop
        }
    } // end afterDatasetsDraw
}; // end fillBetweenLinesPlugin

Chart.pluginService.register(fillBetweenLinesPlugin);

var chartData = {
    labels: [1, 2, 3, 4, 5,6,7,8],
    datasets: [
      {
          label: "Set 1",
          data: [10, 20, null, 40, 30,null,20,40],
          borderColor: "#F00",
          fill: false,
          steppedLine: false,
          tension: 0,
          fillBetweenSet: 1,
          fillBetweenColor: "rgba(255,0,0, 0.2)"
      },
      {
          label: "Set 2",
          data: [60, 40, 10, 50, 60,null,50,20],
          borderColor: "#00F",
          fill: false,
          steppedLine: false,
          tension: 0.5
      },
      {
          label: "Set 2",
          data: [40, 50, 30, 30, 20,null,60,40],
          borderColor: "#0D0",
          fill: false,
          steppedLine: false,
          tension: 0,
          fillBetweenSet: 1,
          fillBetweenColor: "rgba(5,5,255, 0.2)"
      }
    ]
};

var chartOptions = {
    responsive: true,
    title: {
        display: true,
        text: 'Demo Fill between lines'
    }
};

var chartDemo = new Chart($('#demo').get(0), {
    type: 'line',
    data: chartData,
    options: chartOptions
});
</script>
Boley answered 19/1, 2017 at 3:8 Comment(7)
If I click on "Set 1" to hide the curve, the filling stay. Any solutions?Gibeon
@BenjaminLucidarme answer has been revised. Great idea and suggestion.Boley
@Boley Thank for your nice work. I have an other suggestion: try this plugin with these label labels: [1, 2, 3, 4, 5,6,7,8], and these data for each dataset: data: [10, 20, 50, 40, 30,null,20,40],,data: [60, 40, 10, 50, 60,null,50,20],,data: [40, 50, 30, 30, 20,null,60,40],. Filling have a wrong behavior.Gibeon
@BenjaminLucidarme iron sharpens iron. Updated answer and new fiddle here jsfiddle.net/ke5n5LnL/26Boley
How would you change the background color depending on the dataset value ?Renny
@Wes, Have implemented your thoughts on fill in between lines. Thanks. However, when apply pan and zoom options in the chart, the background color overlapping the y-axis i.imgur.com/E8QOEBt.png. Please help to overcome that behavior. Thanks once again.Des
@Siva.Gツ Could you link a jsfiddle?Boley
S
8

Setting fill property to +1 of a dataset will set the backgroundColor from this line to the next line in dataset.

datasets: [{
        label: 'Systolic Guideline',
        data: [],
        fill: '+1',
        borderColor: '#FFC108',
        backgroundColor: 'rgba(255,193,8,0.2)'
      },
      {
        label: 'Diastolic Guideline',
        data: [],
        fill: true,
        borderColor: '#FFC108',
        backgroundColor: 'rgba(0,0,0,0)'
      }]
Smolder answered 9/3, 2020 at 14:4 Comment(2)
Where is the documentation for this feature? I tried and it works but I'm a bit confused about itQuadriplegia
@Quadriplegia see fill property here chartjs.org/docs/latest/charts/line.html#line-stylingSmolder
P
2

On chart.js v2.0 you have this feature now inside. See https://www.chartjs.org/samples/latest/charts/area/line-datasets.html

Pren answered 20/2, 2020 at 13:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.