Javascript canvas animation with initial delay - javascript

I am using requestAnimationFrame in my canvas animation, but I would like to delay the animation for 3 seconds before it starts. I have placed
setTimeout(draw(), 3000);
in many places in my code. Some noted in the comments in the code below:
MyApp.prototype._animation = function() {
var cvs = document.querySelector("#animation");
var ctx = cvs.getContext("2d");
var canvasWidth = cvs.width;
var canvasHeight = cvs.height;
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
var posX = 0;
var posY = 0;
// tried setTimeout here
function draw() {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
ctx.fillRect(100+posX,0,7,canvasHeight); //pole
var instrument = new Path2D();
instrument.moveTo(65+posX,50+posY);
instrument.lineTo(100+posX,50+posY);
instrument.lineTo(100+posX, 10+posY);
instrument.lineTo(65+posX, 10+posY);
instrument.arc(65+posX,30+posY, 20, Math.PI/2, 3*Math.PI/2, false);
instrument.closePath();
var circle = new Path2D();
circle.arc(65+posX, 30+posY, 15, 0, 2 * Math.PI, true);
ctx.globalCompositeOperation = "xor";
ctx.fill(instrument);
ctx.fill(circle);
if (posY < 50){
posY += 1;
} else {
posX += 1;
};
if (posX > 200) {
return;
}
requestAnimationFrame(draw);
}
// tried setTimeout here
draw();
};
I am trying to figure out how to use setTimeout in this case in order to delay the start of the animation.
EDIT: I am trying to figure out WHERE to place setTimeout in this method in order to initially delay the animation.

At the bottom of your script where you call draw(), replace that with:
// Note: no parens in the draw
window.setTimeout(draw,3000);

Related

Canvas code not updating and I'm not sure why

I'm trying to make a simple canvas program where the user clicks to create bouncing moving circles. It keeps freezing but still creates the circles without updating. I'm not sure whats going on, please help!
I'm adding each circle to an array of circles with the constructor
The setInterval loop seems to be freezing but the circles are still created even when this is happening
I'm having a hard time debugging this, any advice is greatly appreciated
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Background Test</title>
<style>
* { margin: 0; padding: 0; overflow: hidden; }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
// Request animation frame -> Optimizes animation speed
const requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
const c = document.getElementById('canvas');
const ctx = c.getContext('2d');
// Fullscreen
c.width = window.innerWidth;
c.height = window.innerHeight;
ctx.fillStyle = 'red';
let fps = 60;
// FOR MOBILE DEVICES
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent))
fps = 29;
// Options
const background = '#333';
const circleMinSpeed = 3;
const circleMaxSpeed = 6;
const circleMinSize = 3;
const circleMaxSize = 10;
const circles = [];
let circlesCounter = 0;
const circlesTimeAlive = 20 * fps; // seconds
let i = 0;
const interval = 1000 / fps;
let now, delta;
let then = Date.now();
// Coordinate variables
let mouseX, mouseY, clickX, clickY;
// Tracks mouse movement
c.onmousemove = function(event)
{
mouseX = event.clientX;
mouseY = event.clientY;
};
// Tracks mouse click
c.onmousedown = function(event)
{
clickX = event.clientX;
clickY = event.clientY;
circle(clickX, clickY);
};
function draw()
{
// Loop
requestAnimationFrame(draw);
// Set NOW and DELTA
now = Date.now();
delta = now - then;
// New frame
if (delta > interval) {
// Update THEN
then = now - (delta % interval);
// Our animation
// Clear canvas then draw
ctx.clearRect(0, 0, c.width, c.height);
drawBackground();
drawCos();
drawCircles();
drawTest();
}
}
// Circle constructor
function circle(x, y)
{
// Pick random color
let r = Math.floor(Math.random() * 255);
let g = Math.floor(Math.random() * 255);
let b = Math.floor(Math.random() * 255);
self.color = 'rgb(' + r + ', ' + g + ', ' + b + ')';
self.xCo = x;
self.yCo = y;
// Pick random size within ranges
self.size = circleMinSize + Math.floor(Math.random() *
(circleMaxSize - circleMinSize));
// Pick random direction & speed (spdX spdY)
self.speed = circleMinSpeed + Math.floor(Math.random() *
(circleMaxSpeed - circleMinSpeed));
self.spdX = self.speed * (Math.random() * 2) - 1; // picks -1 to 1
self.spdY = self.speed * (Math.random() * 2) - 1;
self.draw = function()
{
ctx.beginPath();
ctx.arc(self.xCo, self.yCo, self.size, 0, 2*Math.PI);
ctx.fillStyle = self.color;
ctx.fill();
};
circles[circlesCounter++] = self;
}
// Draw the background
function drawBackground()
{
ctx.fillStyle = background;
ctx.fillRect(0, 0, c.width, c.height);
}
function drawCircles()
{
for (let i = 0; i < circles.length; i++)
circles[i].draw();
}
function drawTest()
{
ctx.fillStyle = 'red';
ctx.fillRect(i++, i, 5, 5);
}
function drawCos()
{
ctx.fillStyle = 'white';
ctx.fillText("X: " + mouseX + " Y:" + mouseY, 10, 10, 200);
}
// Main loop
setInterval(function()
{
// Loop through circles and move them
for (let i = 0; i < circles.length; i++)
{
if (circle[i])
{
// Check left and right bounce
if (circle[i].xCo <= 0 || circle[i].xCo >= c.width)
circle[i].spdX = -circle[i].spdX;
circle[i].xCo += circle[i].spdX;
// Check left and right bounce
if (circle[i].yCo <= 0 || circle[i].yCo >= c.height)
circle[i].spdY = -circle[i].spdY;
circle[i].yCo += circle[i].spdY;
}
}
// Draw Everything
draw();
}, interval);
</script>
</body>
</html>
This code:
self.draw = function()
{
ctx.beginPath();
ctx.arc(self.xCo, self.yCo, self.size, 0, 2*Math.PI);
ctx.fillStyle = self.color;
ctx.fill();
};
Is overriding this function:
function draw()
{
// Loop
requestAnimationFrame(draw);
// Set NOW and DELTA
now = Date.now();
delta = now - then;
// New frame
if (delta > interval) {
// Update THEN
then = now - (delta % interval);
// Our animation
// Clear canvas then draw
ctx.clearRect(0, 0, c.width, c.height);
drawBackground();
drawCos();
drawCircles();
drawTest();
}
}
You need to rethink how you want to draw your circles because you're re-drawing the black canvas every time a click event is triggered. I mean, when a click is triggered, you're applying new coordinates, color, Etc, and probably that's not what you want to do.
My suggestion is create canvas per circle and append them into a DIV.
Hope it helps!

How to move a canvas element up and down for a certain amount of time with javascript

I'm trying to make 6 guitars strings move when they are clicked on. I tried doing a simple animation by moving the string 2.5 px up and 2.5 px down for 3 seconds. but I don't really know how to do that.
This is what I've tried:
function onLoad(){
snaren();
snaarAanraken();
}
function snaren(){
var c = document.getElementById("strings");
var ctx = c.getContext("2d");
for (var i=1;i<7;i++){ //drawing the 6 strings
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(0,42.5*i);
ctx.lineTo(700,42.5*i);
ctx.stroke();
}
}
function snaarAanraken(){
var canvas = document.getElementById("strings");
var ctx = canvas.getContext("2d");
var canvasLeft = canvas.offsetLeft;
var canvasTop = canvas.offsetTop;
canvas.addEventListener('click', function(event) {
var x = event.pageX - canvasLeft;
var y = event.pageY - canvasTop;
console.log(x, y);
if (x >= 0 && x<= 700) {
if (y >= 35 && y<= 50) {//check if clicked on first string
var e = new Audio("e2.mp3");
e.play();
var canvas_y = 42.5;
var bewegen = setInterval(function () {
if (canvas_y > 44) {
canvas_y = 40
}
if (canvas_y < 41) {
canvas_y = 45;
}
ctx.clearRect(0, 0, 700, 60);
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(0,canvas_y);
ctx.lineTo(700,canvas_y);
ctx.stroke();
console.log(canvas_y);
}, 100);
clearInterval(bewegen, 3000);
}
}
For a guitar string you could use quadratic curve coupled with a sine function. The radius for the sine function could be controlled with time.
Also use requestAnimationFrame for good fluid animation. You're not using setTimeout correctly (rAF also provide a high-resolution time as argument which you can use instead of Date.now or performance.now() - not shown).
Example
var ctx = document.querySelector("canvas").getContext("2d");
(function play() {
renderString(75, 20, 5000, function() {
setTimeout(play, 250);
});
})();
// provide y position, max radius, time in ms and a callback function for done
function renderString(y, max, ms, callback) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
f = 0, // fake frequency (demo)
startTime = Date.now();
(function loop() {
var t = 1 - ((Date.now() - startTime) / ms); // normalized time progress
ctx.clearRect(0, 0, w, h); // clear frame
ctx.beginPath(); // new path
ctx.moveTo(0, y); // start of curve
ctx.quadraticCurveTo(w * 0.5, y + getSine() * t, w, y); // quad. curve
ctx.stroke(); // stroke it
if (t > 0) requestAnimationFrame(loop);
else callback();
})();
function getSine() {
return Math.sin((f=f+2)) * max; // todo: use real frequency
}
}
<canvas></canvas>
Here, I made a small example of a vibrating guitar string. It waits 2 seconds then strums the string, which vibrates for half a second.
The problem you have in your code is that you're adding a time-variable to your clearInterval, which isn't possible. If you want a delay on clearing an interval, you need to couple it with setTimeout, kind of like:
setTimeout(function() {
clearInterval(bewegen);
},3000);
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
c.width = 300;
c.height = 400;
var tremble = 2;
var myInterval;
function strum() {
ctx.clearRect(0,0,c.width,c.height);
drawString();
ctx.beginPath();
ctx.moveTo(0,50+tremble);
ctx.lineTo(c.width,50+tremble);
ctx.stroke();
tremble*=-1;
}
function drawString() {
ctx.beginPath();
ctx.moveTo(0,50);
ctx.lineTo(c.width,50);
ctx.stroke();
}
drawString();
setTimeout(function() {
myInterval = setInterval(strum,16);
setTimeout(function() {
clearInterval(myInterval);
ctx.clearRect(0,0,c.width,c.height);
drawString();
},500);
},2000);
<canvas id="canvas"></canvas>

Add same item onto canvas multiple times

basically what I'm trying to do is create a 'raining effect' on the canvas (doesn't exactly look like rain at the moment but I will sort that later)
This is my JavaScript so far:
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas = document.getElementById("canvas");
cx = canvas.getContext("2d");
function rectangle (x, y, w, h) {
var randomx = Math.floor(Math.random() * canvas.width - 50);
var randomy = Math.floor(Math.random() * canvas.height - 100);
this.x = randomx || x || 0;
this.y = randomy || y || 0;
this.w = w || 0;
this.h = h || 0;
this.draw = function () {
cx.fillStyle = "blue";
cx.fillRect(this.x, this.y, this.w, this.h);
};
}
var myRectangle = new rectangle(window.randomx, window.randomy, 10, 10);
function loop () {
cx.clearRect(0, 0, canvas.width, canvas.height);
myRectangle.y++;
myRectangle.draw();
requestAnimFrame(loop);
}
loop();
Basically, it will create a 10 by 10 blue block at a random y and x point of the canvas, what I need to do is keep adding this blue block over and over onto the canvas. I tried including this for loop into the 'loop' function:
for (var i = 0; i < 20; i++) {
var myRectangle = new rectangle(window.randomx, window.randomy, 10, 10);
}
But this just keeps flashing the block at random points (I understand why, it is because it keeps overwriting the variable and placing it at a new point), would anyone be able to help me? I know it would be easier to use jQuery for this, but I'm using JavaScript only
Here is a fiddle for what it looks like at the moment (without the for loop) thanks in advance!
jsfiddle
Make an array of rectangles instead:
myRectangle[i] = new rectangle(...);
This way the previously generated ones won't get overwritten/destroyed.
Here's an example using an array. You also want to make sure you remove them when they're off the screen (note expired variable)
http://jsfiddle.net/8Jqzx/2/
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas = document.getElementById("canvas");
cx = canvas.getContext("2d");
function rectangle (x, y, w, h) {
var randomx = Math.floor(Math.random() * canvas.width - 50);
var randomy = Math.floor(Math.random() * canvas.height - 100);
this.x = randomx || x || 0;
this.y = randomy || y || 0;
this.w = w || 0;
this.h = h || 0;
this.expired = false;
this.draw = function () {
cx.fillStyle = "blue";
cx.fillRect(this.x, this.y, this.w, this.h);
};
this.update = function () {
this.y++;
if (y > canvas.height) {
this.expired = true;
}
}
}
var rectangles = new Array();
function newRect () {
rectangles.push(new rectangle(window.randomx, window.randomy, 10, 10));
}
var timing = 0;
function loop () {
cx.clearRect(0, 0, canvas.width, canvas.height);
if (timing%10 == 0) {
newRect();
}
for (var i = 0; i < rectangles.length; i++) {
rectangles[i].update();
rectangles[i].draw();
if (rectangles[i].expired) {
rectangles.splice(i, 1);
i--;
}
}
timing++;
requestAnimFrame(loop);
}
loop();
But in this one you have them appear at the top (more rain-like): http://jsfiddle.net/8Jqzx/3/

Using JS to animate a circle stroke when in view

I am trying to generate a site with interactive elements and animations. I have came across this Fiddle which is perfect for what I need.
I want the circle to start animating when the canvas element is visible in the browser, or possibly when a certain scroll point has been reached (ie: > 1000 px in the Y direction- start animation).
I am completely new to JS, so have tried to piece together code from other sources with no luck. I have tried (in context with the Fiddle):
// requestAnimationFrame Shim
(function() {
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;
})();
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var endPercent = 85;
var curPerc = 0;
var counterClockwise = false;
var circ = Math.PI * 2;
var quart = Math.PI / 2;
context.lineWidth = 10;
context.strokeStyle = '#ad2323';
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.shadowBlur = 10;
context.shadowColor = '#656565';
function animate(current) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
context.stroke();
curPerc++;
if (curPerc < endPercent) {
requestAnimationFrame(function () {
animate(curPerc / 100)
});
}
}
// My altered code to the original
$(window).scroll(function() {
if ($('#myCanvas').is(':visible')) {
.animate();
}
});
But this is doing nothing. I would imagine this to be a relatively simple problem for those in the know. But I haven't a clue!
Look forward to your answers, apologies if I have missed anything!
You need to include Jquery in your fiddle. That was giving you an error if your going to try and use it for scroll() Also you dont need the . in front of animate(). Also give your body a height that you can scroll on your else youll never activate it. See my fiddle for the changes
http://jsfiddle.net/uhVj6/220/
call your animate(); in this code
$(document).ready(function () {
called = false;
$(window).scroll(function (e) {
if ($(window).scrollTop() > $("canvas").position().top && !called){
animate();
called = true;
}
});
});
http://jsfiddle.net/uhVj6/223/

Simple html5+javascript game flickering

I just started learning javascript, and i already faced some problems. I have a very simple "game" where you move red box with arrow keys and there is black background. I saw some topics about the same question and some of the guys says that make a buffer and other half says that don't do buffer because your browser does it for you. But anyway, I just thought that there is something wrong in my code because it's flickering but it's just so simple that i think it should not.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = 800;
canvas.height = 600;
var mySprite = {
x: 200,
y: 200,
width: 50,
height: 50,
color: '#c00'
};
var yvel = 0;
var xvel = 0 ;
var moved = false;
var mspeed = 15;
var keysDown = {};
window.addEventListener('keydown', function(e) {
keysDown[e.keyCode] = true;
});
window.addEventListener('keyup', function(e) {
delete keysDown[e.keyCode];
});
function update() {
if (37 in keysDown) {
moved = true;
if(xvel > -mspeed){
xvel -= 0.5;
}
}
if (38 in keysDown) {
moved = true;
if(yvel > -mspeed){
yvel -= 0.5
}
}
if (39 in keysDown) {
moved = true;
if(xvel < mspeed){
xvel += 0.5
}
}
if (40 in keysDown) {
moved = true;
if(yvel < mspeed){
yvel += 0.5
}
}
mySprite.x += xvel;
mySprite.y += yvel;
if(moved == false){
xvel = xvel * 0.95;
yvel = yvel * 0.95;
}
moved = false;
}
function render() {
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = mySprite.color;
ctx.fillRect(mySprite.x, mySprite.y, mySprite.width, mySprite.height);
}
function run() {
update();
render();
}
setInterval(run, 1000/60);
You can try it here:w3school
Just copy my code and put it inside tags and change the "MyCanvas" name to "canvas"
The double buffering is in fact allready activated on major browsers.
What you need to do is to get in sync with the screen, that's what's
provide requestAnimationFrame :
https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame
here's the polyfill for requestAnimationFrame :
requestAnimationFrame (rAF) is not standard yet : every 'vendor' i.e. browser (Chrome, FF, Safari, ...) has its own naming.
A polyfill is a piece of code that will 'install' a function in your environement whatever the vendor. Here you will be able to access requestAnimationFrame using window.requestAnimationFrame and you will not care any more about the vendor.
The || operator acts as a 'scan' : it will stop on the first 'truthy' ( == true) expression and return it without evaluating the remaining ones. So the last function definition, for instance, won't get evaluated if msRequestAnimationFrame is defined.
I have to add that the fallback in case requestAnimationFrame is not found is awfull : setTimeout has a very poor accuracy, but thankfully rAF will be found : it is allready there on Chrome(without prefix), Safari (webkit), IE >9 (ms), Firefox (moz) and Opera (o). )
// requestAnimationFrame polyfill
var w=window, foundRequestAnimationFrame = w.requestAnimationFrame ||
w.webkitRequestAnimationFrame || w.msRequestAnimationFrame ||
w.mozRequestAnimationFrame || w.oRequestAnimationFrame ||
function(cb) { setTimeout(cb,1000/60); } ;
window.requestAnimationFrame = foundRequestAnimationFrame ;
and after exchanging update and render (way better), your run loop will be :
function run() {
render();
update();
window.requestAnimationFrame(run);
}
run();
in any case you are interested, i discussed the javascript game loop here :
http://gamealchemist.wordpress.com/2013/03/16/thoughts-on-the-javascript-game-loop/
Why would you need a timer ? the code above will call run again and again, since run ends by a defered call to run : run will be called next time display is available (typically 60 times per seconds).
And you can know about the current time with Date.now().
For more insights about the game loop, rAF, and some time handling issues, see my link above.
Here is your code after some refactoring :
(fiddle is here : http://jsbin.com/uvACEVA/2/edit )
var canvasWidth = 800, canvasHeight = 600;
var canvas = document.getElementById('canvas');
canvas.width = canvasWidth; canvas.height = canvasHeight;
var ctx = canvas.getContext('2d');
// player definition
var mySprite = {
x: 200,
y: 200,
xvel : 0,
yvel : 0,
velIncr : 0.5,
velAttenuation : 0.95,
maxVel : 15,
width: 50,
height: 50,
color: '#c00',
moved : false
};
mySprite.update = function (dt) {
if (keysDown[37]) {
this.moved = true;
if(this.xvel > -this.maxVel){
this.xvel -= this.velIncr;
}
}
if (keysDown[38]) {
this.moved = true;
if(this.yvel > -this.maxVel){
this.yvel -= this.velIncr;
}
}
if (keysDown[39]) {
this.moved = true;
if(this.xvel < this.maxVel){
this.xvel += this.velIncr;
}
}
if (keysDown[40]) {
this.moved = true;
if(this.yvel < this.maxVel){
this.yvel += this.velIncr;
}
}
// to have a frame-rate independant game,
// compute the real dt in run() and ...
this.x += this.xvel; // ... use this.x += this.xvel * dt;
this.y += this.yvel; // ... this.y += this.yvel * dt;
if(this.moved == false){
this.xvel *= this.velAttenuation;
this.yvel *= this.velAttenuation;
}
this.moved = false;
};
mySprite.draw = function(ctx) {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
};
// Keyboard handling
var keysDown = [];
window.addEventListener('keydown', function(e) {
keysDown[e.keyCode] = true;
e.preventDefault();
e.stopPropagation();
});
window.addEventListener('keyup', function(e) {
keysDown[e.keyCode] = false;
e.preventDefault();
e.stopPropagation();
});
// requestAnimationFrame polyfill
var w=window, foundRequestAnimationFrame = w.requestAnimationFrame ||
w.webkitRequestAnimationFrame || w.msRequestAnimationFrame ||
w.mozRequestAnimationFrame || w.oRequestAnimationFrame ||
function(cb) { setTimeout(cb,1000/60); } ;
window.requestAnimationFrame = foundRequestAnimationFrame ;
// main animation loop
function update(dt) {
mySprite.update(dt); // only one for the moment.
}
function render(ctx) {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
mySprite.draw(ctx);
}
var dt = 16; // milliseconds elapsed since last call.
// ideally you should compute it in run(), i didn't for simplicity
function run() {
render(ctx);
update(dt);
window.requestAnimationFrame(run);
}
run();

Categories