3

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

elzi
  • 5,442
  • 7
  • 37
  • 61

1 Answers1

12

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

potatopeelings
  • 40,709
  • 7
  • 95
  • 119
  • Nice methodology! Turn off the offending pieces and plugin the desired pieces using `.pluginService`. – markE May 24 '16 at 16:57
  • Hi there. First off - thank you, amazing work. Few things: 1) There is a slight display bug on the top and bottom (500/0) values on the y axis. Would I be able to fix that within the afterDraw hook or in chartjs options itself. 2) Will this method with pluginServe.register work if there is possibly more than one chart on the same page? Accepting the answer anyway - thanks a ton. – elzi May 24 '16 at 17:33
  • Noticed a few other odds and ends. The newline drawn gridlines having a higher z-index than the points, so the lines go right through them. Also - would you know how to accomplish the white background for the x-axis labels on the bottom? – elzi May 24 '16 at 21:10
  • @elzi - the white background should be fairly simple with `ctx.globalCompositeOperation = 'destination-over'`. Having the gridLines over the chartfill and under the points is, I reckon, a bit messy (both of them get drawn in the same method - better to have the gridLines under the fill and make the fill slightly transparent so that it shows through. I'll give it a shot in a few hours. http://jsbin.com/vahonumosu/1/edit for the white fill (there's a bit of CSS change as well. I'll update the answer when I do the other one) Cheers! – potatopeelings May 24 '16 at 22:39
  • @potatopeelings Thanks for the help. I'm not sure if this is the place for it or if SO allows it (if not, any moderator feel free to delete this comment), but our agency is looking to contract someone to finish these customizations for the chart. If you'd be willing to do such a thing, reach out! Cheers. – elzi May 24 '16 at 22:52
  • @elzi - I'm not sure about the if SO allows it part (from meta, I get the general idea that it's ok to contact someone if they have their contact info on their about About page). I don't freelance, but if it's just a few days of work and your agency is willing to try someone else out, my SO is looking to do contracting work and is on the AU version of freelancer. I can email you the freelancer link if you have a contact email. Cheers! – potatopeelings May 25 '16 at 11:33
  • @potatopeelings Thanks! I updated my profile with a contact email as well. – elzi May 25 '16 at 16:52