Animating an image to create rain effect - javascript

I’m trying to write a simple enough animation with javascript using HTML5 canvas. It’s an image of rain drops that I want to animate seamlessly. This is the image:
https://s31.postimg.org/475z12nyj/raindrops.png
This is how I'm currently animating it:
function Background() {
this.x = 0, this.y = 0, this.w = bg.width, this.h = bg.height;
this.render = function() {
ctx.drawImage(bg, 0, this.y++);
if (this.y <= -199) { //If image moves out of canvas, reset position to 0
this.y = 0;
}
}
}
I’m facing two issues though.
I can’t get the image to loop at all. It just falls down the one time, I need to have this on a loop so it’ll continue again when it starts to leave the canvas.
Once I know how to properly loop it, there is the issue that the rain isn’t suppose to fall down exactly vertically. It needs to fall down diagonally as the raindrops in the image suggest.
This is where it stops to be a simple enough animation.
Here’s my fiddle, it includes all my code. Thanks a lot.
PS: I’ll take any help with either Javascript or CSS I can get. But I do need the rain effect to be using the image only! I can’t accept anything else unfortunately.

I'd recommend splitting out your loop into an animation loop that calls update() and draw() separately. Update your state in update() and then render that state in draw().
Something like this (bit ragged, but you can perhaps make it better :) ):
var lastTick = 0;
var position = { x:0, y:0 };
var bg = document.getElementById('bg');
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
function update(gameTime) {
position.x += (70 * gameTime.diff / 1000);
position.y += (110 * gameTime.diff / 1000);
if (position.x > canvas.width) {
position.x = 0;
}
if (position.y > canvas.height) {
position.y = 0;
}
}
function draw(gameTime) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(bg, position.x, position.y, canvas.width, canvas.height);
ctx.drawImage(bg, position.x - canvas.width, position.y, canvas.width, canvas.height);
ctx.drawImage(bg, position.x, position.y - canvas.height, canvas.width, canvas.height);
ctx.drawImage(bg, position.x - canvas.width, position.y - canvas.height, canvas.width, canvas.height);
}
function loop(tick) {
var diff = tick - lastTick;
var gameTime = { tick:tick, diff:diff };
update(gameTime);
draw(gameTime);
requestAnimationFrame(loop);
lastTick = tick;
}
requestAnimationFrame(loop);
<title>Rain</title>
<meta charset="UTF-8">
<style>
canvas {
width:100vw;
height:100vh;
}
</style>
<img id="bg" src="https://s31.postimg.org/475z12nyj/raindrops.png" style="display:none;">
<canvas id="canvas"><h1>Canvas not supported</h1></canvas>

Related

Drawing a sine wave from top right to bottom left

I'm trying to design a small project using canvas and I want to design a sine wave in such a way that the wave is generated from the top right corner to the left bottom corner infinitely if possible.
Something like an inverse sine graph.
All I can do is make the sine wave go from left to right but making this work from top right to bottom left is very difficult.
This is my code at the moment
It's looking very sad...
const canvas = document.querySelector(".canvas");
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth - 5;
canvas.height = window.innerHeight - 5;
window.addEventListener("resize", () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
const wave = {
y: canvas.height / 2,
length: 0.02,
amplitude: 100,
frequency: 0.01,
yOffSet: canvas.height,
};
let increment = wave.frequency;
function animate() {
requestAnimationFrame(animate);
ctx.fillStyle = "rgb(0,0,0)";
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(0, canvas.height);
for (let i = 0; i < canvas.width; i++) {
ctx.lineTo(Math.sin(i / wave.length + increment) * wave.amplitude + wave.yOffSet, 0);
}
ctx.stroke();
increment += wave.frequency;
}
animate();
body {
margin: 0;
padding: 0;
}
<div>
<canvas class="canvas"></canvas>
</div>
Desired Output
The problem is in this line:
ctx.lineTo(Math.sin(i / wave.length + increment) * wave.amplitude + wave.yOffSet, 0);
You are only moving in the x co-ordinate.
I have added the motion in the y co-ordinate and rewrote on three lines just for clarity.
x=i+wave.amplitude*Math.sin(i/wave.length);
y=canvas.height-(i-(wave. amplitude * Math.sin(i/wave.length)));
ctx.lineTo(x,y);
The result it produces is like what you describe, if I understood correctly. There are many more waves than you show in the drawing, but that can be cahnged by the wave.length parameter.

Javascript canvas flickering

Seems like there are other questions like this and I'd like to avoid a buffer and/or requestAnimationFrame().
In a recent project the player is flickering but I cannot find out the reason. You can find the project on JSFiddle: https://jsfiddle.net/90wjetLa/
function gameEngine() {
timer += 1;
timer = Math.round(timer);
// NEWSHOOT?
player.canShoot -= 1;
// MOVE:
movePlayer();
shootEngine(); // Schussbewegung & Treffer-Abfrage
// DRAW:
ctx.beginPath();
canvas.width = canvas.width;
ctx.beginPath();
ctx.fillStyle = 'black';
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fill();
drawField();
drawPlayer();
drawShoots();
setTimeout(gameEngine, 1000 / 30);
}
Each time you write to a visible canvas the browser want's to update the display. Your drawing routines might be out of sync with the browsers display update. The requestAnimationFrame function allows you to run all your drawing routines before the display refreshes. Your other friend is using an invisible buffer canvas. Draw everything to the buffer canvas and then draw the buffer to the visible canvas. The gameEngine function should only run once per frame and if it runs multiple times you could see flicker. Try the following to clear multiple runs in the same frame.
(edit): You might also want to clear the canvas instead of setting width.
(edit2): You can combine the clearRect, rect, and fill to one command ctx.fillRect(0, 0, canvas.width, canvas.height);.
var gameEngineTimeout = null;
function gameEngine() {
// clear pending gameEngine timeout if it exists.
clearTimeout(gameEngineTimeout);
timer += 1;
timer = Math.round(timer);
// NEWSHOOT?
player.canShoot -= 1;
// MOVE:
movePlayer();
shootEngine(); // Schussbewegung & Treffer-Abfrage
// DRAW:
ctx.beginPath();
//canvas.width = canvas.width;
//ctx.clearRect(0, 0, canvas.width, canvas.height);
//ctx.beginPath();
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
//ctx.fill();
drawField();
drawPlayer();
drawShoots();
gameEngineTimeout = setTimeout(gameEngine, 1000 / 30);
}

Growing infinite circles with JS and Canvas

I want to make a simple page that grows circles from its center ad infinitum. I'm almost there, but I can't figure out how to repeatedly grow them (resetting the radius i to 0 at a certain interval and calling the function again). I assume it will require a closure and some recursion, but I can't figure it out.
// Initialize canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.getElementsByTagName('body')[0].appendChild(canvas);
// Grow a circle
var i = 0;
var draw = function() {
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.height / 2, i, 0, 2 * Math.PI);
ctx.fill();
i += 4;
window.requestAnimationFrame(draw);
}
draw();
Two things I'd do...
First, modify your draw function so that if the circle gets to a certain size, the i variable is reset back to zero. That starts the circle over again.
Second, add a setInterval timer to call your draw function at some time interval. See http://www.w3schools.com/js/js_timing.asp for details.
This setup will cause draw() to be called regularly, and the reset of i to zero makes it repeat.
So this did indeed require a closure. We wrap the initial function in a closure, and call it's wrapper function, which reinitializes I every time when called. draw() grows a single circle, and drawIt()() starts a new circle.
var drawIt = function(color) {
var i = 0;
return function draw() {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(canvas.width/2, canvas.height/2, i, 0, 2 * Math.PI);
ctx.fill();
i+=1*growthFactor;
// Growing circles until they are huge
if (i < canvas.width) {
window.requestAnimationFrame(draw);
if (i === spacing) {
circles++
drawIt(nextColor())();
}
}
}
};
drawIt(nextColor())();
})();

Canvas not clearing at original position in Android

I'm working with phonegap and trying to make simple app for Android, where a ball rolls around based on the accelerometer. I'm using a canvas that takes up the whole screen and drawing a circle for the ball.
Everything works fine, except that the canvas is not getting cleared properly between each frame. It clears everything except the ball at the original position. So there are always 2 balls, one moving around, and one stationary in the middle.
Here is my render function
function render(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(posX, posY, size, 0, 2*Math.PI, false);
ctx.fillStyle = 'red';
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.stroke();
}
I've tried using canvas.width = canvas.width, but this simply moves the fake ball to the real balls current position. It also gives really poor performance if I do this every frame.
This problem doesn't happen when I use a browser (Chrome) and move the ball with the console.
The really weird thing is that it doesn't always happen. Sometimes in rare cases, when i start the app or use canvas.width, the fake ball disappears.
Edit:
Tested with different devices, and the problem is only on my Samsung Galaxy Note (Android 4.1.2), while it works fine on my HTC Desire HD (Android 2.3.5) and iPhone 5 (iOS 7.1)
Full code
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady(){
navigator.accelerometer.watchAcceleration(onSuccess, onError, { frequency: 100 });
}
function onSuccess(acc){
accX = -acc.x;
accY = acc.y;
}
function onError(message){
alert('Failed because: ' + message);
}
var size = 40;
var speed = 80;
var posX;
var posY;
var speedX;
var speedY;
var accX;
var accY;
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = screen.width / window.devicePixelRatio;
canvas.height = screen.height / window.devicePixelRatio;
document.body.appendChild(canvas);
reset();
var then = Date.now();
setInterval(main, 1);
function reset(){
posX = canvas.width/2;
posY = canvas.height/2;
speedX = 0;
speedY = 0;
accX = 0;
accY = 0;
}
function update(mod){
speedX += accX*mod;
speedY += accY*mod;
posX += speedX*mod*speed;
posY += speedY*mod*speed;
if((posX < 0-size) || (posX > canvas.width+size) || (posY < 0-size) || (posY > canvas.height+size))
reset();
}
function render(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(posX, posY, size, 0, 2*Math.PI, false);
ctx.fillStyle = 'red';
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.stroke();
}
function main(){
var now = Date.now();
var delta = now - then;
update(delta/1000);
render();
then = now;
}
Instead of clearing the canvas, I tried filling it with white.
context.fillStyle = "#FFFFFF";
context.fillRect(0, 0, canvas.width, canvas.height);
I tested it on a LG P880, CyanogenMod 10.1, with its stock Android browser, and the original canvas disappeared afterwards.

Making a circle that moves

Been trying to get something to work in HTML and I haven't been able to nail it down quite yet. Basically, I want to create a canvas and then make a circle inside the canvas that moves from edge of the canvas to edge of the canvas. Any suggestions?
EDIT:
Folks wanted what I have so far, so here it is:
<html>
<head>
<script type="text/javascript">
function draw () {
var canvas = document.getElementById('circle');
if (canvas.getContext) {
var context = canvas.getContext('2d');
context.fillStyle = "rgb(150,29,28)";
var startPoint = (Math.PI/180)*0;
var endPoint = (Math.PI/180)*360;
context.beginPath();
context.arc(200,200,150,startPoint,endPoint,true);
context.fill();
context.closePath();
}
}
}
</script>
</head>
<body onload="init();">
<canvas id="canvas" width="500" height="500"></canvas><br>
</body>
</html>
I'm not really sure how to get a circle onto the canvas (and I'm still shakey on the implementation thereof) as well as how to make it, y'know, move. I have examples of how to rotate something, but not really how to move it. Sorry for the inexperience guys, trying to learn HTML on my own, but the book I've got doesn't seem really descriptive on this aspect, even though it's a supposed to be teaching me HTML.
So up to now you have a code where you're able to draw a circle at a certain position onto the canvas surface, very well, now in order to make it look like it's moving you'll have to keep drawing it again and again with it's position slightly changed to give a smooth motion effect to it, it's a standard to draw things 60 times per second (I think that's because 60 frames per second is the most that the human eye can notice, or whatever). And of course, each time you draw it on another position, it's going to be necessary to clear the older drawing.
Let's change your code just a bit in order to make it animation-friendly:
<script type="text/javascript">
function init()
{
canvas = document.getElementById('canvas');
if(canvas.getContext)
context = canvas.getContext('2d');
else return;
setInterval(draw, 1000 / 60); // 60 times per second
}
function draw()
{
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "rgb(150,29,28)";
// var startPoint = (Math.PI/180)*0; Kinda redundant, it's just 0
// var endPoint = (Math.PI/180)*360; Again, it's just PI times 2
context.beginPath();
context.arc(200, 200, 150, 0, Math.PI * 2, true);
context.fill();
context.closePath();
}
</script>
</head>
<body onload="init();">
<canvas id="canvas" width="500" height="500"></canvas><br>
Now there are lots of funny ways to make an object move towards a fixed point, but the simplest is, of course, moving along a straight line. To do so you'll need
A vector containing the object's current position
A vector containing the object's target position
Let's change your code so we have those at hand
var canvas, context,
position = {x: 200, y: 200},
target = {x: 400, y: 400};
function init()
{
...
}
function draw()
{
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "rgb(150,29,28)";
context.beginPath();
context.arc(position.x, position.y, 150, 0, Math.PI * 2, true);
context.fill();
context.closePath();
}
Good! This way whenever you change one of the values in the position object, the position of your circle is going to be affected.
If you subtract the current position from the target position you'll get another vector which points straight to the target position coming from the current position right? So get that vector and just normalize it, turning it's length to 1, the result will actually be the (cosine, sine) of the angle from the target to the object's position. What that means is if you add that normalized vector to the object's current position, the object will move towards the target position 1 unit at a time.
Normlizing a vector is simply dividing it's components by it's length.
function normalize(v)
{
var length = Math.sqrt(v.x * v.x + v.y * v.y);
return {x: v.x / length, y: v.y / length};
}
var step = normalize({x: target.x - position.x, y: target.y - position.y});
Ok, now all we need to do is keep adding the step vector to the object's current position until it reaches the target position.
function normalize(v)
{
var length = Math.sqrt(v.x * v.x + v.y * v.y);
return {x: v.x / length, y: v.y / length};
}
var canvas, context,
position = {x: 200, y: 200},
target = {x: 400, y: 400},
step = normalize({x: target.x - position.x, y: target.y - position.y});
function init()
{
canvas = document.getElementById('canvas');
if(canvas.getContext)
context = canvas.getContext('2d');
else return;
setInterval(draw, 1000 / 60);
}
function draw()
{
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "rgb(150,29,28)";
context.beginPath();
context.arc(position.x, position.y, 150, 0, Math.PI * 2, true);
context.fill();
context.closePath();
position.x += step.x;
position.y += step.y;
}
And there you have it. Of course it's pretty basic code, you'll have to add a codition to check whether or not the object has arrived to the target otherwise it will just keep going past the target. If it's moving too slow, just scale the step vector by an arbitrary speed factor. Also in a real world app you'd have lot's of objects and not only just a circle so you'd have to make it entirely object-oriented since every object would have it's position, target position, color etc etc etc. A library to work with vectors would come in handy. This is one I wrote for myself some time ago: http://pastebin.com/Hdxg8dxn
Here you go, give this a crack -
<!DOCTYPE html>
<html>
<head>
<script>
function byId(e){return document.getElementById(e);}
function newEl(tag){return document.createElement(tag);}
function newTxt(txt){return document.createTextNode(txt);}
function toggleClass(element, newStr)
{
index=element.className.indexOf(newStr);
if ( index == -1)
element.className += ' '+newStr;
else
{
if (index != 0)
newStr = ' '+newStr;
element.className = element.className.replace(newStr, '');
}
}
function forEachNode(nodeList, func)
{
var i, n = nodeList.length;
for (i=0; i<n; i++)
{
func(nodeList[i], i, nodeList);
}
}
window.addEventListener('load', mInit, false);
var canvas, hdc;
var posx=50, posy=0, radius=50;
function circle(hdc, x, y, radius)
{
hdc.beginPath();
hdc.arc(x, y, radius, 0, 2*Math.PI);
hdc.stroke();
//arc(x,y,r,start,stop)
}
function mInit()
{
canvas = byId('tgtCanvas');
hdc = canvas.getContext('2d');
//circle(hdc, posx, posy, 50);
setInterval(animateStep, 50);
}
var velX = 2;
function animateStep()
{
posx += velX;
if (posx+radius > canvas.width)
velX *= -1;
else if (posx-radius < 0)
velX *= -1;
hdc.clearRect(0,0,canvas.width,canvas.height);
circle(hdc, posx, posy, radius);
}
</script>
<style>
</style>
</head>
<body>
<canvas id='tgtCanvas' width='256' height='256'></canvas>
</body>
</html>

Categories