Html5 - Canvas Element - Multiple Layers

html5 - canvas element - Multiple layers

No, however, you could layer multiple <canvas> elements on top of each other and accomplish something similar.

<div style="position: relative;">
<canvas id="layer1" width="100" height="100"
style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
<canvas id="layer2" width="100" height="100"
style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
</div>

Draw your first layer on the layer1 canvas, and the second layer on the layer2 canvas. Then when you clearRect on the top layer, whatever's on the lower canvas will show through.

3 layered canvas in Html5

Multiple canvas layers can definitely be a good thing for performance! If your background layer only needs to be drawn once each scene, then it allows you to redraw your foreground lots of times, without having to worry about wasting time redrawing the background.

However, they need to be layered rather than nested.

<div id = "container" class = "container">
<canvas id = "canvas_background" width = "800" height = "500"></canvas>
<canvas id = "canvas_trans" width = "800" height = "500"></canvas>
<canvas id = "canvas_foreground" width = "800" height = "500"></canvas>
</div>

Thankfully, this is trivial with CSS. We can use absolute positioning and take advantage of the fact that DOM elements are transparent by default.

.container {
position: relative;
}

.container > canvas {
position: absolute;
top: 0;
left: 0;
}

This will set all canvas elements to be absolutely positioned within the container element.

The last thing you'll need to remember is that to clear the canvases, you'll have to use the context.clearRect method, in order to return the canvas to transparency, rather than filling it with a solid colour.

Layering The Canvas Without Multiple Canvases

If you store the text, second image and first image in variables, you could just draw them on the canvas in the order you prefer. This means that, whenever there's some change in the image or text, you should clean the canvas and redraw everything.

Also, you may be interested in Compositing, since it allows you to decide where a new object is drawn in relation to the existing drawing (on top, behind, etc.)

Implementing Layers in HTML5 Canvas

If you want to use a single canvas element and have multiple layers inside it, you might want to look at my library:

  • https://github.com/ant512/CanvasLayers

It uses a damaged rect system to reduce the amount of repainting done every time the canvas changes, so not only do you get layers (which can be nested), but you also get optimised redraws.

HTML5 Canvas game, multiple layers issue

Create a spritesheet that contains all the images necessary to animate your "oil well" tile.

Then you can pull the sprites off that spritesheet and overlay your rendered map on that single oil well tile.

Then you're rendering the large map once. You're rendering just that tile multiple times (a more efficient task than re-rendering the entire map just to animate the one tile). You can use a second overlaid canvas for best performance. You could even do an overlaid canvas that's just the size of a single sprite and use CSS positioning to move it over the target tile.

Example code and a Demo:

var canvasT=document.getElementById("canvasTop");var ctx=canvasT.getContext("2d");var canvasB=document.getElementById("canvasBottom");var ctxB=canvasB.getContext("2d");var cw,ch;
var nextTime=0;var duration=1000/60*3;//var spritesheetWidth=256;var spritesheetHeight=256;var spriteCols=4;var spriteRows=4;var spriteCount=spriteRows*spriteCols;var spritePosition=0;var spriteWidth=spritesheetWidth/spriteCols;var spriteHeight=spritesheetHeight/spriteRows;var fireX=265;var fireY=100;//var imgCount=2;var fire=new Image();fire.onload=start;fire.src='https://dl.dropboxusercontent.com/u/139992952/stackoverflow/explodeSprite.png'var map=new Image();map.onload=start;map.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/isometric1.png";function start(){ if(--imgCount>0){return;} cw=canvasB.width=canvasT.width=map.width; ch=canvasB.height=canvasT.height=map.height; canvasB.width=map.width; canvasB.height=map.height; ctxB.drawImage(map,0,0);
animate();}

function animate(time) { if(time<nextTime){requestAnimationFrame(animate);return;}
nextTime=time+duration;
var row=parseInt(spritePosition/spriteCols); var col=spritePosition-row*spriteCols; var spX=col*spriteWidth; var spY=row*spriteHeight;
// Drawing code goes here ctx.clearRect(0,0,cw,ch); ctx.drawImage(fire, spX,spY,spriteWidth,spriteHeight, fireX,fireY,spriteWidth,spriteHeight);
spritePosition++; if(spritePosition>spriteCount-1){ spritePosition=0; }
requestAnimationFrame(animate);}
body{ background-color: ivory; }#wrapper{  position:relative;  width:1095px;  height:655px;
}#canvasTop,#canvasBottom{ position:absolute; top:0px; left:0px; border:1px solid green; width:100%; height:100%;}#canvasTop{ border:1px solid red;}
<div id="wrapper">  <canvas id="canvasBottom"></canvas>  <canvas id="canvasTop"></canvas></div>

Clearing canvas layers separately?

If you look at things from a performance point-of-view, things are better if you use a single visible <canvas> element for your visual output.
Nothing is stopping you from doing things on seperate canvases you stack on top of each other though. Maybe there's just a basic misunderstanding here.

You say:

and when I do clearRect on one of my layers it does nothing as the
pixels are also drawn on the main canvas in addition to given layer

Well that's not true. If you draw the contents of a freshly cleared canvas onto another canvas it won't overwrite the target canvas with 'nothing'.

Take a look at this example:

let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
ctx.fillStyle = "green";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.lineWidth = 10;
ctx.arc(canvas.width / 2, canvas.height / 2, 50, 0, 2 * Math.PI);
ctx.stroke();

let tempCanvas = document.createElement("canvas");
let tempContext = tempCanvas.getContext("2d");
tempContext.clearRect(0, 0, tempCanvas.width, tempCanvas.height);
ctx.drawImage(tempCanvas, 0, 0, canvas.width, canvas.height);
<canvas id="canvas"></canvas>

Issue with Layering Canvases using z-index

First time answering on SO, but I believe I have the answer to your question.

I redid the HTML and JavaScript to be as simplistic as possible, since your question seems to be at the root of it all, asking for help on changing canvas layering, so I did it as simply as I could think of, here is the code, followed by a link to the codepen:

<!DOCTYPE html>

<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Canvas Layering</title>
</head>

<body>
<div style="position: relative;">
<canvas id="layer1" width="100" height="100"
style="position: absolute; left: 0; top: 0; z-index: 0; background-image: url(https://static.pexels.com/photos/158780/leaf-nature-green-spring-158780.jpeg);" onclick="ChangePicture(1)"></canvas>
<canvas id="layer2" width="100" height="100"
style="position: absolute; left: 0; top: 0; z-index: 1; background-image: url(https://static.pexels.com/photos/54320/rose-roses-flowers-red-54320.jpeg);" onclick="ChangePicture(2)"></canvas>
<canvas id="layer3" width="100" height="100"
style="position: absolute; left: 0; top: 0; z-index: 2; background-image: url(https://static.pexels.com/photos/5412/water-blue-ocean.jpg);" onclick="ChangePicture(3)"></canvas>
</div>
</body>

<script>

function ChangePicture(layerNumber) {
if (layerNumber === 1) {
document.getElementById("layer1").style.zIndex = "0";
document.getElementById("layer2").style.zIndex = "2";
document.getElementById("layer3").style.zIndex = "1";
} else if (layerNumber === 2) {
document.getElementById("layer1").style.zIndex = "1";
document.getElementById("layer2").style.zIndex = "0";
document.getElementById("layer3").style.zIndex = "2";
} else if (layerNumber === 3) {
document.getElementById("layer1").style.zIndex = "2";
document.getElementById("layer2").style.zIndex = "1";
document.getElementById("layer3").style.zIndex = "0";
} else {
console.log("Failed.");
}
}

</script>

</html>

https://codepen.io/levi_blodgett/pen/KQyWoR

If you want to change what picture starts as the default, simply change the z-index for the styling inside the html.



Related Topics



Leave a reply



Submit