Text and animation together on HTML5 canvas

1.3k Views Asked by At

I have an animation based canvas that on mouseover animates rain droplets and the animation stops on mouseout. I have textbox which on submit should show text on canvas. However this text disappears when i moveout and mouseover again. I know that the canvas is redrawn on mouseover but i am unable to figure how to make the text remain where it is. Thanks!

I have adapted the code from the solution provided here => Random images falling like rain in canvas (Javascript)

Javascript

var ctx;
var imgBg;
var imgDrops;
var x = 0;
var y = 0;
var noOfDrops = 7;
var fallingDrops = [];
var intV;

imgBg = new Image();
imgBg.src = "image.jpg";
var canvas = document.getElementById('canvasRegn');
ctx = canvas.getContext('2d');
ctx.drawImage(imgBg,0,0,600,450); //Background

function draw() {
    ctx.drawImage(imgBg, 0, 0,600,450); //Background

    for (var i=0; i< noOfDrops; i++)
    {
    ctx.drawImage (fallingDrops[i].image, fallingDrops[i].x, fallingDrops[i].y); //The rain drop

    fallingDrops[i].y += fallingDrops[i].speed;
    fallingDrops[i+4].x += fallingDrops[i].speed-1;//Set the falling speed
    if (fallingDrops[i].y > 450) {  //Repeat the raindrop when it falls out of view
    fallingDrops[i].y = -120; //Account for the image size
    fallingDrops[i].x = Math.random() * 600;    //Make it appear randomly along the width    
    }

    }
}

function setup() {

    intV = setInterval(function(){draw()}, 36);
    for (var i = 0; i < noOfDrops; i++) {
        var fallingDr = new Object();
        fallingDr["image"] =  new Image();
        fallingDr.image.src = "Rain.svg";       
        fallingDr["x"] = Math.random() * 600;
        fallingDr["y"] = Math.random() * 5;
        fallingDr["speed"] = 3 + Math.random() * 5;
        fallingDrops.push(fallingDr);

        }
}
function start(){
setup();
}

function stop(){
    clearInterval(intV);
}

function clicked(){
    var x=document.getElementById("form_val");
    ctx.clearRect(0,0,600,400);
    ctx.font="36px Verdana";
    ctx.fillStyle="yellow";
    ctx.strokeStyle="green";
    ctx.lineWidth=2;
    ctx.strokeText(x.value,200,200);
    ctx.fillText(x.value,200,200);
}

HTML

<canvas id="canvasRegn" width="600" height="450"style="margin:10px;" onmouseover="start()" onmouseout="stop()">
</canvas>
<br>
<input type="text" name="fname" size="50" id="form_val">
<button id="submit" onclick="clicked()">Submit</button>
2

There are 2 best solutions below

0
On BEST ANSWER

The answer to this question lies in the laying of two canvas layers. First Canvas layer will have the background image and the animation effect. Second layer on top of it will draw the Text. Note : Credit to @DBS for finding the solution.

JavaScript:

script type="text/javascript">
var ctx;
var ctx2
var imgBg;
var imgDrops;
var x = 0;
var y = 0;
var noOfDrops = 20;
var fallingDrops = [];
var intV;
fontVar ="36px Verdana";
fillColour="yellow";
strokeColour="green";

imgBg = new Image();
imgBg.src = "image.jpg";
var canvas = document.getElementById('myCanvas');
ctx = canvas.getContext('2d');
ctx.drawImage(imgBg,0,0,600,400); //Background

var canvas2 = document.getElementById('drawText');
ctx2 = canvas2.getContext('2d');

function drawing(){
ctx.drawImage(imgBg,0,0,600,400); //Background
}

function draw() {
    ctx.drawImage(imgBg, 0, 0,600,400); //Background    
    for (var i=0; i< noOfDrops; i++)
    {

    ctx.drawImage (fallingDrops[i].image, fallingDrops[i].x, fallingDrops[i].y,35,35); //The rain drop

    fallingDrops[i].y += fallingDrops[i].speed;
    fallingDrops[i+7].x += fallingDrops[i].speed-1;//Set the falling speed
    if (fallingDrops[i].y > 400) {  //Repeat the raindrop when it falls out of view
        fallingDrops[i].y = -120; //Account for the image size
        fallingDrops[i].x = Math.random() * 600;    //Make it appear randomly along the width    
        }
    }       
}


function setup() {

    intV = setInterval(function(){draw()}, 36);
    for (var i = 0; i < noOfDrops; i++) {
        var fallingDr = new Object();
        fallingDr["image"] =  new Image();
        fallingDr.image.src = "Rain.svg";       
        fallingDr["x"] = Math.random() * 600;
        fallingDr["y"] = Math.random() * 5;
        fallingDr["speed"] = 3 + Math.random() * 5;
        fallingDrops.push(fallingDr);

        }
}
function start(){
setup();
}

function stop(){
    clearInterval(intV);
}

function clicked(){
    z=document.getElementById("form_val");
    ctx2.clearRect(0,0,600,400);
    ctx2.font=fontVar;
    ctx2.fillStyle=fillColour;
    ctx2.strokeStyle=strokeColour;
    ctx2.lineWidth=2;
    ctx2.strokeText(z.value,200,200);
    ctx2.fillText(z.value,200,200);
}

</script>

HTML

<div class="wrapper">
<canvas id="myCanvas" width="600" height="400" style="margin:1px;"></canvas>
<canvas id="drawText" width="600" height="400" onmouseover="start()" onmouseout="stop()</canvas>
</div>
<br>
Greeting Message: <input type="text" name="fname" size="50" id="form_val">
<button id="submit" onclick="clicked()">Add this message</button>
</div>

CSS

How can I stack two same-sized canvas on top of each other?

10
On

Each time you redraw the canvas you need to redraw the textbox, I would personally rename "clicked()" and call it from inside "draw()" (either before or after the drops depending on whether you want it to appear above or below)

You'd also have to remove the ctx.clearRect() from "clicked()" or it will overwrite the rain (if you're placing it on top)

Then you'd need to edit how it was called, the clicked() function could set a boolean variable which is checked inside the draw function (and if true, draws the textbox)

Pseudo code example:

var text = false
draw(){
    drawRain()
    if(text == true){drawText()}
}
clicked(){
    text = true
}

Then if you wanted the textbox to be editable, you can use variables instead of fixed values in the drawText() e.g.

Outside the drawText()

fontVar = "36px Verdana";
fillColour = "yellow";
strokeColour = "green";

Inside the drawText()

ctx.font=fontVar;
ctx.fillStyle=fillColour;
ctx.strokeStyle=strokeColour;