zkropotkine
zkropotkine

Reputation: 337

How to add an offset to a dataset in Chart js

I was able to add an offset to the X Labels but I would like to add an offset to all the points in the dataset. Is it possible?

Chart

This is the code I'm using:

var myChart = new Chart.Line(ctx, {
    type: 'line',
    data: {
        labels: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC", ""],
        datasets: [{
            data: [5, 10.5, 18.2, 33.9, 121.2, 184.9, 179.9, 196.1, 158.3, 166.3, 66.4, 20.6, null],
            pointLabelFontSize : 4,
            borderWidth: 2,
            fill: false,
            lineTension: .3,
            borderColor: "#f37029",
            borderCapStyle: 'round',
            borderDash: [],
            borderDashOffset: 0.0,
            borderJoinStyle: 'bevel',
            pointBorderColor: "#f37029",
            pointBackgroundColor: "#f37029",
            pointBorderWidth: 1,
            pointHoverRadius: 4,
            pointHoverBackgroundColor: "rgba(220,220,220,1)",
            pointHoverBorderColor: "rgba(220,220,220,1)",
            pointHoverBorderWidth: 2,
            pointRadius: 4,
            pointHitRadius: 10,
            spanGaps: false,
        }]
    },
    options: {
        scales: {
            xAxes: [{
                gridLines: {
                    offsetGridLines: true,
                    display: false,
                    borderDash: [6, 2],
                    tickMarkLength:5
                },
                ticks: {
                     fontSize: 8,
                     labelOffset: 10,
                     maxRotation: 0
                }}],
            yAxes: [{
                gridLines: {
                    display:false
                },
                ticks: {
                    beginAtZero: true,
                    max: 200,
                    min: 0,
                    stepSize: 20,
                    fontSize: 8
                }
            }]
        },
        legend: {
            display: false
        },
        responsive: false,
        maintainAspectRatio: true
    }
});

I would like to apply that offset to all the points, in the image I just added an arrow to the JAN/DEC but I would like to apply it to all of them.

I tried adding a null data, the problem is that I don't want to show the first dashed grid.

enter image description here

Any ideas? Thanks in advance.

Upvotes: 11

Views: 31474

Answers (4)

pierreben
pierreben

Reputation: 396

Inspired from tektiv answer but updated for Chartjs v3 or v4, you can use a plugin on beforeDraw and resize events to achieve it :

<canvas id="canvas"></canvas>

<script>
var ctx = document.getElementById("canvas");

var myChart = new Chart(ctx, {
  "type": "bar",
  "data": {
      "labels": [
          "Label 1",
          "Label 2",
          "Label 3",
          "Label 4",
          "Label 5",
          "Label 6",
          "Label 7",
          "Label 8",
          "Label 9",
          "Label 10",
          "Label 11",
      ],
      "datasets": [
          {
              "label": "Dataset 1",
              "data": [123, 12, 19, 9, 1, 5, 4, 1, 4, 4, 0],
              "backgroundColor": "#881000",
              "barThickness": 30,
              "xAxisID": "x-axis1",
          },
          {
              "label": "Dataset 2",
              "data": [2729, 152, 119, 81, 51, 33, 32, 26, 25, 13, 6],
              "backgroundColor": "#d95e4e",
              "barThickness": 30,
              "xAxisID": "x-axis1",
          },
          {
              "label": "Dataset 1 bis",
              "data": [223, 24, 40, 20, 2, 10, 8, 2, 8, 8, 2],
              "backgroundColor": pattern.draw("diagonal", "#a194ff"),
              "barThickness": 30,
              "xAxisID": "x-axis2",
          },
          {
              "label": "Dataset 2 bis",
              "data": [3000, 300, 250, 180, 100, 66, 60, 50, 50, 26, 12],
              "backgroundColor": pattern.draw("diagonal", "#cec8ff"),
              "barThickness": 30,
              "xAxisID": "x-axis2",
          },
      ],
  },
  "options": {
      "scales": {
          "x-axis1": {"stacked": true},
          "x-axis2": {"stacked": true, display:false},
      },
  },
  "plugins": [{
    "beforeDraw": function(chart) {
          chart.data.datasets.forEach((dataset, i) => {
              if (i > 1) {
                  const meta = chart.getDatasetMeta(i);
                  meta.data.forEach((bar, index) => {
                    if(bar.old_x === undefined){
                      bar.old_x = bar.x;
                    }
                    bar.x = bar.old_x + 15;
                  });
              }
      });
    },
    "resize": function(chart) {
      chart.data.datasets.forEach((dataset, i) => {
              if (i > 1) {
                  const meta = chart.getDatasetMeta(i);
                  meta.data.forEach((bar, index) => {
                    if(bar.old_x !== undefined){
                      bar.old_x = undefined;
                    }
                  });
              }
      });
    }
  }]
});
</script>

enter image description here

You can have a look at this jsfiddle for Chartjs v3 and this jsfiddle for Chartjs v4

Upvotes: 0

OJB1
OJB1

Reputation: 2785

Leading on from the answer given by 'tektiv' I needed a similar solution but one that works for RESPONSIVE CHARTS.

So instead of using fixed measurements for the given offset shown in tektiv's plugin, we first count the number of objects in the dataset array. We then divide the chart.width by the number of objects in the array to give us equal segments, then in order to define the half way point between each grid line, we divide that sum by a factor of 2.

Note 1: You could also replace the factor of 2 to a variable so the user could chose the portion of offset needed.

Note 2: I've placed the plugin code within the chart script given I don't want this as a global affect by registering a global plugin.

Note 3: This is second re-edit of my solution given the plugin code I partially copied from the answer given by 'tektiv' above was only firing successfully for the first time, but then when re-loading a new instance of the chart I experienced some null errors on the dataset._meta (worth also seeing answer here on this particular topic as this helped me fix and finalize my answer: Dataset._meta[0].dataset is null in ChartJS Code example:

<script>

    var myChart;

    function drawChart() {

        var ctx = document.getElementById('myChart').getContext('2d');
        ctx.innerHTML = '';

        if (myChart != null) {
            myChart.destroy();
        }

        var datasetArray = [5, 10.5, 18.2, 33.9, 121.2, 184.9, 179.9, 196.1, 158.3, 166.3, 66.4, 20.6, null];

        myChart = new Chart(ctx, {
            type: 'line',
            data: {
                labels: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC", ""],
                datasets: [{
                    data: datasetArray,
                    borderWidth: 2,
                    borderColor: "#f37029",
                    pointBorderColor: "#f37029",
                    pointBackgroundColor: "#f37029",
                    pointHitRadius: 10,
                    spanGaps: false,
                }]
            },
            plugins: [{
                afterUpdate: function (chart, options) {
                    //..
                    var dataset = chart.config.data.datasets[0];

                    // Get the number of objects in the dataset array.
                    var noDataPoints = datasetArray.length;

                    //alert(noDataPoints); // testing only, you'll notice that this 
                    // alert would fire each time the responsive chart is resized.
                    var xOffset = (chart.width / noDataPoints) / 2;

                    for (var i = 0; i < dataset.data.length; i++) {
                        for (var key in dataset._meta) {
                            var model = dataset._meta[key].data[i]._model;
                            model.x += xOffset;
                            model.controlPointNextX += xOffset;
                            model.controlPointPreviousX += xOffset;
                        }
                    }
                }
            }],
            options: {
                scales: {
                    xAxes: [{
                        gridLines: {
                            offsetGridLines: false,
                            display: true,
                        },
                        ticks: {
                            fontSize: 8,
                            maxRotation: 0
                        }
                    }],
                    yAxes: [{
                        gridLines: {
                            display: true
                        },
                        ticks: {
                            beginAtZero: true,
                        }
                    }]
                },
                legend: {
                    display: false
                },
                responsive: true,
                maintainAspectRatio: true
            }
        });
    }

</script>

First screenshot below shows the responsive chart stretched to a wide screen view:

wide screen

Second screenshot shows the responsive chart resized to a smaller and more conventional window size:

narrow screen

Upvotes: 3

Jason Yang
Jason Yang

Reputation: 157

Check out - http://www.chartjs.org/docs/latest/axes/cartesian/ .

In chapter "Common Configuration",there is a Boolean attribute offset. Default value is false (except in case of bar chart)

If true, extra space is added to the both edges and the axis is scaled to fit into the chart area. This is set to true in the bar chart by default.

So you can just set it to true, and it should work.

Upvotes: 10

tektiv
tektiv

Reputation: 14187

You can achieve this using Chart.js plugins. They let you handle specific events triggered during the chart creation (beforeInit, afterUpdate, afterDraw ...) and are also easy to implement :

Chart.pluginService.register({
    afterUpdate: function(chart) {
        // This will be triggered after every update of the chart
    }
});

Now you just have to loop through your dataset data model (the property used to draw charts) and add the offset you want :

Chart.pluginService.register({
    afterUpdate: function(chart) {
        // We get the dataset and set the offset here
        var dataset = chart.config.data.datasets[0];
        var offset = 20;

        // For every data in the dataset ...
        for (var i = 0; i < dataset._meta[0].data.length; i++) {
            // We get the model linked to this data
            var model = dataset._meta[0].data[i]._model;

            // And add the offset to the `x` property
            model.x += offset;

            // .. and also to these two properties
            // to make the bezier curve fits the new graph
            model.controlPointNextX += offset;
            model.controlPointPreviousX += offset;
        }
    }
});

You can see your example working on this jsFiddle and here is its result :

enter image description here

Upvotes: 6

Related Questions