8

I have asked a question before: How can I control z-index of canvas objects? and we reached to a solution that may not be a good one for complicated situations.

I found that canvas doesn't have a z-index system, but a simple ordered drawing one. Now there is a new question: how can I simulate z-index system to make this problem fixed in complicated situations?

The good answer can solve a big problem.

Community
  • 1
  • 1
JalalJaberi
  • 2,417
  • 8
  • 25
  • 41
  • 1
    Well you could use a library like kineticJS (It has support for z-index among others) here take a look http://www.html5canvastutorials.com/kineticjs/html5-canvas-shape-layering-with-kineticjs/ – ppsreejith Jan 11 '13 at 07:13
  • Yes, I do use kineticJS in some cases. I am trying to find how can I do it myself. – JalalJaberi Jan 11 '13 at 07:14

2 Answers2

11

It's not that canvas doesn't have a z-index, it's that canvas doesn't keep objects drawn contrary to the HTML page. It just draws on the pixel matrix.

There are basically two types of drawing models :

  • object ones (usually vector) : objects are kept and managed by the engine. They can usually be removed or changed. They have a z-index
  • bitmap ones : there are no objects. You just change a pixel matrix

The Canvas model is a bitmap one. To have objects drawn over other ones, you must draw them after. This means you must manage what you draw.

The canvas model is very fast, but if you want a drawing system managing your objects, maybe you need SVG instead.


If you want to use a canvas, then the best is to keep what you draw as objects. Here's an example I just made : I keep a square list and every second I randomize their zindex and redraw them :

var c = document.getElementById('c').getContext('2d');
function Square(x, y, s, color) {
   this.x = x; this.y = y; this.s = s; this.color = color;
   this.zindex=0;
}
Square.prototype.draw = function(c) {
  c.fillStyle = this.color;
  c.fillRect(this.x, this.y, this.s, this.s);  
}
var squares = [
  new Square(10, 10, 50, 'blue'), new Square(40, 10, 40, 'red'), new Square(30, 50, 30, 'green'),
  new Square(60, 30, 40, '#111'), new Square(0, 30, 20, '#444'), new Square(70, 00, 40, '#999')
];

function draw() {
  c.fillStyle = "white";
  c.fillRect(0, 0, 1000, 500);
  for (var i=0; i<squares.length; i++) squares[i].draw(c);
}
setInterval(function(){
  // give all squares a random z-index
  squares.forEach(function(v){v.zindex=Math.random()});
  // sort the list accordingly to zindex
  squares.sort(function(a,b){return a.zindex-b.zindex});
  draw();
}, 1000);

Demonstration

The idea is that the square array is sorted accordingly to zindex. This could be easily extended to other types of objects.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • Thanks, but have you seen that question? I know that it is possible now, but in a manner that make code dirty. I am searching for a clean way. – JalalJaberi Jan 11 '13 at 07:07
  • @JalalJaberi Is it clearer with the example ? I give a z-index to all my squares. – Denys Séguret Jan 11 '13 at 07:34
  • 1
    @JalalJaberi: this is as clean as you'll get it when using the canvas. You will have to sort the elements you're going to draw. This requires no less than the code dystroy answered with. – Cerbrus Jan 11 '13 at 07:37
  • Yes, there is no problem with shapes, but when images enter the scene there is some latency to load. Because of this latency we will be forced to draw anything in image onload event. Is that true? – JalalJaberi Jan 11 '13 at 07:39
  • Or wait all images to load. That makes life hard. – JalalJaberi Jan 11 '13 at 07:40
  • 1
    @JalalJaberi This is a different question, and the answer should depend on the context, but basically "not always". If you draw periodically, you could just test if the image is yet loaded at each draw. When I use many images, I also make sure I use a sprite system (i.e. I embed them all in a bigger image so that there is only one request). – Denys Séguret Jan 11 '13 at 07:41
1

As dystroy has said, z-index is, at its simplest, just an index to tell you in what order to draw things on the canvas, so that they overlap properly.

If you mean to do more than this, say to replicate the existing workings of a browser, then you would have more work to do. The order in which objects are drawn in a browser is a complicated calculation that is driven by:

  1. The DOM tree
  2. Elements' position attributes
  3. Elements' z-index attributes

The canonical source to this is the Elaborate description of Stacking Contexts, part of the CSS specification.

Ian Clelland
  • 43,011
  • 8
  • 86
  • 87
  • Thanks, if it is possible with DOM I am very pleased to being familiar with. That's nice. – JalalJaberi Jan 11 '13 at 07:17
  • Sorry, I don't mean to say that you can control your canvas output in any way with the DOM -- I mean that the browser's use of z-index for regular html elements is more complicated than you might think. If you want to replicate HTML-like stacking in canvas, though, that's your best reference. – Ian Clelland Jan 11 '13 at 07:20
  • Got it. I will try to do such a business. – JalalJaberi Jan 11 '13 at 07:25