Jez
Jez

Reputation: 165

Javascript Canvas Draw Order

I am plotting a bar chart with some series values in a javascript loop. Even though the code drawing the series values lies below the code that draws the bars, in the next iteration of the loop the text that has just been drawn gets overwritten.

You can see this effect taking place in the code below as the alert boxes appear.

Why do drawing operations on another part of the canvas overwrite something drawn previously in a completely different place?

Update: Someone has helpfully pointed out that using fillRect() hides this issue, but my question is rather: why is it happening in the first place?

var exampleData = {
  "Series 1": 10,
  "Series 2": 14,
  "Series 3": 2,
  "Series 4": 12
};

var BarChart = function(options) {
  this.options = options;
  this.canvas = options.canvas;
  this.ctx = this.canvas.getContext("2d");
  this.colors = options.colors;

  this.plot = function() {
    var maxValue = 0;
    for (var categ in this.options.data) {
      maxValue = Math.max(maxValue, this.options.data[categ]);
    }

    var noBars = Object.keys(this.options.data).length;
    var barWidth = (this.canvas.height) / noBars;
    var barIdx = 0;

    for (categ in this.options.data) {
      var barLength = Math.round(this.canvas.width * this.options.data[categ] / maxValue);
      this.ctx.save();
      alert("plotting series line " + categ);
      this.ctx.fillStyle = this.colors[barIdx % this.colors.length];
      this.ctx.rect(30, barIdx * barWidth, barLength, barWidth);
      this.ctx.fill();
      alert("plotting series value " + categ);
      this.ctx.fillStyle = "#000000";
      this.ctx.font = "24px Georgia";
      this.ctx.textBaseline = "middle";
      this.ctx.fillText(this.options.data[categ], 25, barIdx * barWidth + barWidth / 2); //will be covered in the loop's next iteration. Why?
      this.ctx.restore();
      barIdx++;
    }
  }
}

function init() {
  var myCanvas = document.getElementById("myCanvas");
  myCanvas.width = 800;
  myCanvas.height = 300;
  var ctx = myCanvas.getContext("2d");
  var myBarChart = new BarChart({
    canvas: myCanvas,
    seriesName: "Example Series",
    padding: 40,
    data: exampleData,
    colors: ["#D1E3F3", "#D1E3F3", "#D1E3F3", "#D1E3F3"]
  });
  myBarChart.plot();
}
document.addEventListener("DOMContentLoaded", init, false);
<canvas id="myCanvas"></canvas>

Upvotes: 0

Views: 2421

Answers (1)

Brett Gregson
Brett Gregson

Reputation: 5913

Changing :

this.ctx.rect(30, barIdx * barWidth, barLength, barWidth);
this.ctx.fill();

To instead use fillRect() fixes the problem:

this.ctx.fillRect(30, barIdx * barWidth, barLength, barWidth);

Working example here (compared to the original example here)

Upvotes: 1

Related Questions