intoitoverit
intoitoverit

Reputation: 47

Beginner at HTML5 Canvas, using layers?

I'm new to the HTML5 canvas, and I'm trying to use layers. I've looked for ways to implement them, but haven't had anything that helps what I'm trying to do. I am trying to make an animation of a volcano eruption. I want to put the volcano and background sky-blue color onto one layer, the lava falling from the sky onto a second layer, and the ash clouds on a third layer. I already had worked on this when I found out about layers, so I know things are out of place. My question though is am I on the right track when it comes to implementing the layers?

<body>
<div id="canvasesdiv" style="position:relative; width:400px; height:300px">
<canvas id="layer1" style="z-index: 1; position:absolute; left:0px; top:0px;" width="800" height="500"></canvas>
<canvas id="layer2" style="z-index: 2; position:absolute; left:0px; top:0px;" width="800" height="500"></canvas>
<canvas id="layer3" style="z-index: 3; position:absolute; left:0px; top:0px;" width="800" height="500"></canvas>
</div>
</body>

<script>

//var canvas = document.getElementById("myCanvas");
//var context = canvas.getContext("2d");
var layer1;
var layer2;
var layer3;
var particles;
var eruption;
var timer;
var timerRestart;

function init(){
    layer1 = document.getElementById("layer1");
    ctx1 = layer1.getContext("2d");
    layer2 = document.getElementById("layer2");
    ctx2 = layer2.getContext("2d");
    layer3 = document.getElementById("layer3");
    ctx3 = layer3.getContext("2d");

}

function animationHandler(){
    fillBackgroundColor(canvas, context);
    drawVolcano();
    drawClouds();
    eruption = setTimeout(makeParticles, 10);
}

function drawClouds(){

    ctx3.beginPath();
    ctx3.moveTo(0, 100);
    ctx3.bezierCurveTo(0, 100, 75, 200, 150, 100);
    ctx3.bezierCurveTo(150, 100, 225, 200, 300, 85);
    ctx3.bezierCurveTo(300, 85, 375, 200, 450, 75);
    ctx3.bezierCurveTo(450, 75, 525, 200, 600, 100);
    ctx3.bezierCurveTo(600, 100, 700, 200, 800, 100);
    ctx3.lineTo(800, 0);
    ctx3.lineTo(0, 0);
    ctx3.closePath();

    ctx3.fillStyle = "#6f2a2a";
    ctx3.fill();

    ctx3.lineWidth = 5;
    ctx3.strokeStyle = "#371515";
    ctx3.stroke();

}

function drawVolcano(){

    ctx1.beginPath();
    ctx1.moveTo(0, 400);
    ctx1.bezierCurveTo(0, 400, 250, 400, 325, 200);
    ctx1.lineTo(425, 200);
    ctx1.bezierCurveTo(425, 200, 450, 400, 800, 400);
    ctx1.lineTo(800, 500);
    ctx1.lineTo(0, 500);
    ctx1.closePath();

    ctx1.fillStyle = "#802b00";
    ctx1t.fill();

    ctx1.lineWidth = 5;
    ctx1.strokeStyle = "#b33c00";
    ctx1.stroke();

}

function fillBackgroundColor(canvas, context){
    ctx1.fillStyle = "#3399ff" ;
    ctx1.fillRect(0, 0, canvas.width, canvas.height);
}

function makeParticles() {
//create an array of particles for our animation
    particles = [];
    for(var i = 0; i < 100; i++)
    {
        particles.push(new Particle());
    }
}

function degreesToRadians(degrees) {
 //converts from degrees to radians and returns
    return (degrees * Math.PI)/180;
}

function Particle(){
 //the constructor for a single particle, with random starting x+y, velocity, color, and radius
 //this.x = Math.random()*canvas.width;
 //this.y = Math.random()*canvas.height;
    this.x = canvas.width/2;
    this.y = (0,0);
    this.vx = Math.random()*16-8;
    this.vy = Math.random()*10;
    var colors = ["red", "#ff6600", "yellow", "#262626"];
    this.color = colors[Math.floor(Math.random()*colors.length)];
    this.radius = 50;
}

function moveParticles() {
 //partially clear the screen to fade previous circles, and draw a new particle at each new coordinate
    ctx2.globalCompositeOperation = "source-over";
    ctx2.fillStyle = "rgba(0, 0, 0, 0.3)";
    ctx2.fillRect(0, 0, canvas.width, canvas.height);
    ctx2.globalCompositeOperation = "lighter";
    for(var i = 0; i < particles.length; i++)
    {
        var p = particles[i];
        ctx2.beginPath();
        ctx2.arc(p.x, p.y, p.radius, 0, degreesToRadians(360), true);
        ctx2.fillStyle = p.color;
        ctx2.fill();
        p.x += p.vx;
        p.y += p.vy;
        if(p.x < -50) p.x = canvas.width+50;
        if(p.y < -50) p.y = canvas.height+50;
        if(p.x > canvas.width+50) p.x = -50;
        if(p.y > canvas.height+50) p.y = -50;
        p.radius -= 1;
    }
}

function clearScreen(color) {
 //clears the screen and fills with the color of choice
    ctx2.clearRect(0, 0, canvas.width, canvas.height);
    ctx2.fillStyle = color;
    ctx2.fillRect(0, 0, canvas.width, canvas.height);
}

window.onload = function() {
    animationHandler();
    timer = setInterval(moveParticles, 60);
    //timerRestart = setInterval(makeParticles, 4000);
}

EDIT: So my issue before and why I started to try to use layers was because the animation of the lava would black out the canvas. So I thought if I separated everything onto different layers, it wouldn't black out the canvas. I implemented Sergio's code down in the answers and it's close to what I'm trying to achieve, but I still am getting the issue from before. I was referencing some code for the eruption animation and that's just how the animation works itself. I guess I should probably put this into a separate question, but is there a different way to get the lava to work to where it will still have the same effect as now, but maybe to where it's canvas' opacity is down so you can see the layer underneath? This is where the animation happens:

function moveParticles() {
//partially clear the screen to fade previous circles, and draw a new particle at each new coordinate
ctx2.globalCompositeOperation = "source-over";
ctx2.fillStyle = "rgba(0, 0, 0, 0.3)";
ctx2.fillRect(0, 0, canvas.width, canvas.height);
ctx2.globalCompositeOperation = "lighter";

Upvotes: 1

Views: 2288

Answers (1)

Sergio Fernandez
Sergio Fernandez

Reputation: 381

your on the right path. but there are a few issues. first on windows.load you start by calling animationHandler, without ever calling init(), which results in a lot of errors. then there is a lot of references to a variable called canvas which has not been defined, well; it was but you commented it out. and there are one or two typing error(drawVolcano() has a typing error), which prevent the code from execution.

fixing a few bugs: and adding the init on windows.load you get an animation that works: try this, let me know if this is what you wanted. if it is, then you did it correctly.. you just had a few small bugs.

<body>
<div id="canvasesdiv" style="position:relative; width:800px; height:500px">
<canvas id="layer1" style="z-index: 1; position:absolute; left:0px; top:0px;" width="800" height="500"></canvas>
<canvas id="layer2" style="z-index: 2; position:absolute; left:0px; top:0px;" width="800" height="500"></canvas>
<canvas id="layer3" style="z-index: 3; position:absolute; left:0px; top:0px;" width="800" height="500"></canvas>
</div>
<script>

//var canvas = document.getElementById("myCanvas");
//var context = canvas.getContext("2d");
var layer1;
var layer2;
var layer3;
var particles;
var eruption;
var timer;
var timerRestart;

function init(){
    layer1 = document.getElementById("layer1");
    ctx1 = layer1.getContext("2d");
    layer2 = document.getElementById("layer2");
    ctx2 = layer2.getContext("2d");
    canvas=layer3 = document.getElementById("layer3");
    context=ctx3 = layer3.getContext("2d");

}

function animationHandler(){
    fillBackgroundColor(canvas, context);
    drawVolcano();
    drawClouds();
    eruption = setTimeout(makeParticles, 10);
}

function drawClouds(){

    ctx3.beginPath();
    ctx3.moveTo(0, 100);
    ctx3.bezierCurveTo(0, 100, 75, 200, 150, 100);
    ctx3.bezierCurveTo(150, 100, 225, 200, 300, 85);
    ctx3.bezierCurveTo(300, 85, 375, 200, 450, 75);
    ctx3.bezierCurveTo(450, 75, 525, 200, 600, 100);
    ctx3.bezierCurveTo(600, 100, 700, 200, 800, 100);
    ctx3.lineTo(800, 0);
    ctx3.lineTo(0, 0);
    ctx3.closePath();

    ctx3.fillStyle = "#6f2a2a";
    ctx3.fill();

    ctx3.lineWidth = 5;
    ctx3.strokeStyle = "#371515";
    ctx3.stroke();

}

function drawVolcano(){

    ctx1.beginPath();
    ctx1.moveTo(0, 400);
    ctx1.bezierCurveTo(0, 400, 250, 400, 325, 200);
    ctx1.lineTo(425, 200);
    ctx1.bezierCurveTo(425, 200, 450, 400, 800, 400);
    ctx1.lineTo(800, 500);
    ctx1.lineTo(0, 500);
    ctx1.closePath();

    ctx1.fillStyle = "#802b00";
    ctx1.fill();

    ctx1.lineWidth = 5;
    ctx1.strokeStyle = "#b33c00";
    ctx1.stroke();
}

function fillBackgroundColor(canvas, context){
    ctx1.fillStyle = "#3399ff" ;
    ctx1.fillRect(0, 0, canvas.width, canvas.height);
}

function makeParticles() {
//create an array of particles for our animation
    particles = [];
    for(var i = 0; i < 100; i++)
    {
        particles.push(new Particle());
    }

}

function degreesToRadians(degrees) {
 //converts from degrees to radians and returns
    return (degrees * Math.PI)/180;
}

function Particle(){
 //the constructor for a single particle, with random starting x+y, velocity, color, and radius
 //this.x = Math.random()*canvas.width;
 //this.y = Math.random()*canvas.height;
    this.x = canvas.width/2;
    this.y = (0,0);
    this.vx = Math.random()*16-8;
    this.vy = Math.random()*10;
    var colors = ["red", "#ff6600", "yellow", "#262626"];
    this.color = colors[Math.floor(Math.random()*colors.length)];
    this.radius = 50;
}

function moveParticles() {
 //partially clear the screen to fade previous circles, and draw a new particle at each new coordinate
    ctx2.globalCompositeOperation = "source-over";
    ctx2.fillStyle = "rgba(0, 0, 0, 0.3)";
    ctx2.fillRect(0, 0, canvas.width, canvas.height);
    ctx2.globalCompositeOperation = "lighter";
    for(var i = 0; i < particles.length; i++)
    {
        var p = particles[i];
        ctx2.beginPath();
        ctx2.arc(p.x, p.y, p.radius, 0, degreesToRadians(360), true);
        ctx2.fillStyle = p.color;
        ctx2.fill();
        p.x += p.vx;
        p.y += p.vy;
        if(p.x < -50) p.x = canvas.width+50;
        if(p.y < -50) p.y = canvas.height+50;
        if(p.x > canvas.width+50) p.x = -50;
        if(p.y > canvas.height+50) p.y = -50;
        p.radius -= 1;
    }

}

function clearScreen(color) {
 //clears the screen and fills with the color of choice
    ctx2.clearRect(0, 0, canvas.width, canvas.height);
    ctx2.fillStyle = color;
    ctx2.fillRect(0, 0, canvas.width, canvas.height);

}

window.onload = function() { 
    init();
    animationHandler();
    timer = setInterval(moveParticles, 60);
    //timerRestart = setInterval(makeParticles, 4000);
}
</script>
</body>

on my browser however, the particle moved from the top to the bottom. if you want a volcano effect. it has to move in an arc. shooting up from the volcano then falling down. however, you are on the right track, just need to change the code a little bit more.

I modified the code a bit more a few minutes ago, to add an arc type motion of the fireball, and to use some sprites.(http://www.dominicanvoice.com/test/volcano.php)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>

<body>
<div id="canvasesdiv" style="position:relative; width:800px; height:500px">
<canvas id="layer1"  style="z-index: 1; position:absolute; left:0px; top:0px;" width="800" height="500"></canvas>
<canvas id="layer1f" style="z-index: 1; position:absolute; left:0px; top:0px;" width="800" height="500"></canvas>
<canvas id="layer2"  style="z-index: 2; position:absolute; left:0px; top:0px;" width="800" height="500"></canvas>
<canvas id="layer2f"  style="z-index: 2; position:absolute; left:0px; top:0px;" width="800" height="500"></canvas>
<canvas id="layer3"  style="z-index: 3; position:absolute; left:0px; top:0px;" width="800" height="500"></canvas>
</div>
<script>

//var canvas = document.getElementById("myCanvas");
//var context = canvas.getContext("2d");
var layer1;
var layer2;
var layer3;
var particles;
var eruption;
var timer;
var timerRestart;

function init(){
    layer1 = document.getElementById("layer1");
    ctx1 = layer1.getContext("2d");
    layer1f = document.getElementById("layer1f");
    f1 = layer1f.getContext("2d");
    layer2 = document.getElementById("layer2");
    ctx2 = layer2.getContext("2d");
    layer2f = document.getElementById("layer2f");
    f2 = layer2f.getContext("2d");
    canvas=layer3 = document.getElementById("layer3");
    context=ctx3 = layer3.getContext("2d");

}

function animationHandler(){
    fillBackgroundColor(canvas, context);
    drawVolcano();
    //drawClouds();
    eruption = setTimeout(makeParticles, 10);
}


var cloud = new Image();
cloud.src='http://www.dominicanvoice.com/test/volcano/cloud.png';

var fireball = new Image();
fireball.src='http://www.dominicanvoice.com/test/volcano/fireball.png';

function drawVolcano(){

    img = new Image();
    img.src='http://www.dominicanvoice.com/test/volcano/layer2.png';
    img.onload = function() { ctx1.drawImage(this,0,0);};

    img2 = new Image();
    img2.src="http://www.dominicanvoice.com/test/volcano/front.png";
    img2.onload = function() { ctx2.drawImage(this,0,0);};
}

function fillBackgroundColor(canvas, context){
    ctx1.fillStyle = "#3399ff" ;
    ctx1.fillRect(0, 0, canvas.width, canvas.height);
}

function makeParticles() {
//create an array of particles for our animation
    particles = [];
    for(var i = 0; i < 100; i++)
    {
        particles.push(new Particle());
    }

}

function degreesToRadians(degrees) {
 //converts from degrees to radians and returns
    return (degrees * Math.PI)/180;
}

function Particle(){
 //the constructor for a single particle, with random starting x+y, velocity, color, and radius
 //this.x = Math.random()*canvas.width;
 //this.y = Math.random()*canvas.height;
    this.x = canvas.width/2;
    this.y = 160;
    this.vx = Math.random()*16-8;
    this.vy = Math.random()*25;
    var colors = ["red", "#ff6600", "yellow", "#262626"];
    this.color = colors[Math.floor(Math.random()*colors.length)];
    this.radius = 50;
}

function moveParticles() {
 //partially clear the screen to fade previous circles, and draw a new particle at each new coordinate
    f1.clearRect(0, 0, canvas.width, canvas.height);
    f2.clearRect(0, 0, canvas.width, canvas.height);
    for(var i = 0; i < particles.length; i++)
    {
        var p = particles[i];
        f=(p.vy>0) ?f1:f2;
        if (p.vy>0) f.drawImage(cloud,Math.floor((25-p.vy)/2)*128,0,128,128,p.x-64,p.y,128,128) 
        else ctx3.drawImage(cloud,1536,0,128,128,p.x-Math.random()*5-64,-20+Math.random()*10,128,128);

        if (p.y<500) { 

        f.drawImage(fireball,Math.floor(-p.vy/2)*128,0,128,64,p.x-64,p.y,128,64); 

   //     f.beginPath();
    //    f.arc(p.x, p.y, p.radius, 0, degreesToRadians(360), true);
    //    f.fillStyle = p.color;
    //    f.fill();
        p.x += p.vx;
        p.y -= p.vy;
        p.vy-=1;
        if(p.x < -50) p.x = canvas.width+50;
        if(p.y < -50) p.y = canvas.height+50;
        if(p.x > canvas.width+50) p.x = -50;
        if(p.y > canvas.height+50) p.y = -50;
        p.radius -= 1;
        };
    }

}


window.onload = function() { 
    init();
    animationHandler();
    timer = setInterval(moveParticles, 60);
    //timerRestart = setInterval(makeParticles, 4000);
}
</script>
</body>

The code has not been cleaned up or optimized, but you can look at it modify it and play with it. you can see it working here: http://www.dominicanvoice.com/test/volcano.php NOTE: I did not test this code on a Mac. only on windows and it worked fine.

Upvotes: 1

Related Questions