Chart.js 2.0 - How to change default appearance of canvas/chart elements

elzi picture elzi · May 19, 2016 · Viewed 11.5k times · Source

I'm using the new Chart.js and trying to accomplish several customizations. Versed in JS but new to canvas, I'm struggling a bit. I'll try to provide as much information as possible.

 

Related Links

 

Code - JSBin

JavaScript

/**
 * Chart.js Global Config
 */
Chart.defaults.global.pointHitDetectionRadius = 5;
window.count = 0;




/**
 * Chart Data
 * @type {Object}
 */
var lineChartData = {
  labels: ["", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC", ""],
  datasets: [{
    label: "Students",
    data: [ 200, 250,220,180,290,300,370,350,200,280,260,190,210, 200 ],
    backgroundColor: "rgba(247,155,45,1.0)",
    borderColor: "rgba(247,155,45,1.0)",
    borderCapStyle: 'butt',
    borderDash: [],
    borderDashOffset: 0.0,
    pointBorderColor: "rgba(245,245,245,1)",
    pointBackgroundColor: "rgba(80,81,81,1)",
    pointHoverBorderWidth: 5,
    pointBorderWidth: 5,
    pointRadius: 8,
    pointHoverRadius: 9,
    pointHitRadius: 8,
  }]
};



/**
 * Init
 */
window.onload = function() {

  var $chart = $('#chart');

  window.lineChart = new Chart($chart[0], {
    type: 'line',

    data: lineChartData,

    options: {
      showLines: true,

      // Legend
      legend : {
        display: false
      },

      // title
      title:{
        display:false,
        text:'Student Hours'
      },

      // Tooltips
      tooltips: {
        enabled: false,
      },

      // Scales
      scales: {
        yAxes: [{
          id: 'y-axis-0',
          gridLines: {
            display: true,
            lineWidth: 1,
            color: "rgba(255,255,255,0.85)"
          },
          ticks: {
            beginAtZero:true,
            mirror:false,
            suggestedMin: 0,
            suggestedMax: 500,
          },
          afterBuildTicks: function(chart) {

          }
        }],
        xAxes: [{
          id: 'x-axis-0',
          gridLines: {
            display: false
          },
          ticks: {
            beginAtZero: true
          }
        }]
      },
    }
  });

};

HTML

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.bundle.min.js"></script>
  <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>

</head>
<body>
  <div id="chartjs-container" class="chartjs-wrap">
    <canvas id="chart"></canvas>
  </div>
</body>
</html>

CSS

#chartjs-container {
  width:80%;
  margin:20px auto;
  position: relative;
}

.chartjs-wrap {
  background-color: rgba(250, 210, 162, 1.0);
}

  canvas {
    -moz-user-select: none;
    -webkit-user-select: none;
    -ms-user-select: none;

    height:100%;
  }

 

What I'm trying to do

  1. Remove the grid line up the y-axis
  2. Remove the points on the first and last items of the dataset that meet the left/right edge of the chart
  3. Inset the y-axis scale labels
  4. Make x-axis grid lines overlay on top of the line data fill

 

Screenshots

Current State current-state-ss

Desired State (Approximate) desired-state-ss

 

Any help pointing me in the right direction would be great. Is there an equivalent on "Inspect Element" for canvas? These fixes would be trivial in CSS but I'm unsure on how to debug.

Cheers

Answer

potatopeelings picture potatopeelings · May 24, 2016

1.Remove the grid line up the y-axis

Just set display to false for options.scales.yAxes. This will remove all the labels too - we'll call the library method to draw the labels (without drawing the y-axis) in the plugin (see Step 4)


2.Remove the points on the first and last items of the dataset that meet the left/right edge of the chart

Just pass in an array to pointRadius and pointHoverRadius instead of a number. The following arrays will hide the first and last points (with your data)

...
pointRadius: [0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0],
pointHoverRadius: [0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0],
...

You might have to generate this using a script if your data length is dynamic.


3.Inset the y-axis scale labels

Set ticks.mirror for options.scales.yAxes to true. The plugin (from Step 4) will then just call the library draw method for the scale.


4.Make x-axis grid lines overlay on top of the line data fill

To make the gridLines appear (like it is) over the fill but under the points, the easiest way would be to draw it under the fill and set the fill to a slightly transparent value.

backgroundColor: "rgba(247,155,45,0.4)",

We have to draw the gridLines on our own since we don't want them to start from the edge. So set gridLines.display to false for options.scales.yAxes and register the following plugin

Chart.pluginService.register({
    afterDraw: function (chart, easingDecimal) {
        var yScale = chart.scales['y-axis-0'];
        var helpers = Chart.helpers;
        var chartArea = chart.chartArea;

        // draw labels - all we do is turn on display and call scale.draw
        yScale.options.display = true;
        yScale.draw.apply(yScale, [chartArea]);
        yScale.options.display = false;

        yScale.ctx.save();
            // draw under the fill
        yScale.ctx.globalCompositeOperation = 'destination-over';
        // draw the grid lines - simplified version of library code
        helpers.each(yScale.ticks, function (label, index) {
            if (label === undefined || label === null) {
                return;
            }

            var yLineValue = this.getPixelForTick(index);
            yLineValue += helpers.aliasPixel(this.ctx.lineWidth);

            this.ctx.lineWidth = this.options.gridLines.lineWidth;
            this.ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)';

            this.ctx.beginPath();
            this.ctx.moveTo(chartArea.left + 40, yLineValue);
            this.ctx.lineTo(chartArea.right, yLineValue);
            this.ctx.stroke();

        }, yScale);
        yScale.ctx.restore();
    },
})

Plugins will be called for all charts. If you want to skip the above logic for some charts, all you have to is set some property on the options object and check for it before you run your logic (e.g. yAxis.options.custom at the same level as yAxis.options.display


If you want to hide the 0 and 500 labels, you can set ticks.callback for options.scales.yAxes, like so

callback: function(value) {
  if (value !== 0 && value !== 500)
    return '' + value;
},

Note that it works as long as your scale is within the suggestedMin and suggestedMax. If your data goes outside these bounds, you'll have to use the scale properties.


To make the x-axis label background white, just add the below bit to the end of the plugin's afterDraw method

yScale.ctx.save();
yScale.ctx.fillStyle = 'white';
yScale.ctx.globalCompositeOperation = 'destination-over';
yScale.ctx.fillRect(0, yScale.bottom, chartArea.right, chartArea.bottom);
yScale.ctx.restore();

It just draws a white rectangle under the canvas content. Because your background color is set via the CSS, the rectangle is on top of the background color and everything is shiny.

You also have to move your background-color from your .chartjs-wrap to your canvas (otherwise you get a orange border at the bottom)

canvas {
    background-color: rgba(250, 210, 162, 1.0);
    ...

Updated version of your JSBin will all the above applied - http://jsbin.com/parucayara/1/edit?output