HTML5 - delay canvas drawing - javascript

I have a simple chunk of code to draw a line in a page. My problem is that I don't know much about HTML5 or JS and I need help to set a delay on the drawing of this line.
I want to be able to choose if I want to see it drawing instantly when opening the page or define it to have 5 seconds delay before being draw.
Here it is:
<canvas id="myCanvas" width="1250" height="120"></canvas>
<script>
var canvas = $("#myCanvas")[0];
var c = canvas.getContext("2d");
var amount = 0;
var startX = 164;
var startY = 120;
var endX = 1094;
var endY = 120;
setInterval(function() {
amount += 0.01; // change to alter duration
if (amount > 1) amount = 1;
c.clearRect(0, 0, canvas.width, canvas.height);
c.strokeStyle = "black";
c.lineWidth=1;
c.strokeStyle="#707070";
c.moveTo(startX, startY);
// lerp : a + (b - a) * f
c.lineTo(startX + (endX - startX) * amount, startY + (endY - startY) * amount);
c.stroke();
}, 0);
</script>
Thank you for the help!

Wrap it in a setTimeout:
var canvas = $("#myCanvas")[0];
var c = canvas.getContext("2d");
var amount = 0;
var startX = 164;
var startY = 120;
var endX = 1094;
var endY = 120;
setTimeout(function() {
var interval = setInterval(function() {
amount += 0.01; // change to alter duration
if (amount > 1) {
amount = 1;
clearInterval(interval);
}
c.clearRect(0, 0, canvas.width, canvas.height);
c.strokeStyle = "black";
c.lineWidth=1;
c.strokeStyle="#707070";
c.moveTo(startX, startY);
// lerp : a + (b - a) * f
c.lineTo(startX + (endX - startX) * amount, startY + (endY - startY) * amount);
c.stroke();
}, 0);
}, 3000);
The above waits 3 seconds (3000 milliseconds) before starting the drawing. Also, whenever you start an interval with setInterval you should store the return value so you can stop the interval later. The code above stops the interval when it's done drawing with clearInterval().

Wrap your setInterval call in a setTimeout call. setInterval invokes its function argument repeatedly, where the second argument specifies the delay between invocations. setTimeout invokes its function argument once, after the delay has passed.
function redraw() {
amount += 0.01; // change to alter duration
if (amount > 1) amount = 1;
c.clearRect(0, 0, canvas.width, canvas.height);
c.strokeStyle = "black";
c.lineWidth=1;
c.strokeStyle="#707070";
c.moveTo(startX, startY);
// lerp : a + (b - a) * f
c.lineTo(startX + (endX - startX) * amount, startY + (endY - startY) * amount);
c.stroke();
}
setTimeout(function () { setInterval(redraw, 0) }, 5000);

You need to use setTimeout:
setTimeout(function() {
setInterval(function() {
amount += 0.01; // change to alter duration
if (amount > 1) amount = 1;
c.clearRect(0, 0, canvas.width, canvas.height);
c.strokeStyle = "black";
c.lineWidth=1;
c.strokeStyle="#707070";
c.moveTo(startX, startY);
// lerp : a + (b - a) * f
c.lineTo(startX + (endX - startX) * amount, startY + (endY - startY) * amount);
c.stroke();
}, 0);
}, 5000);

You need to use setTimeout. setTimeout runs a call after a certain delay.
The function used in your script, setInterval, runs the same function over and over again at a certain interval. Strile's answer should help you

Short answer
Use setTimeout function to delay execution. What setTimeout does is set up a timer which will execute specified function after specified amount of time. E.g.
setTimeout(function() {
alert("Hello!");
}, 5000);
will show an alert after 5 seconds (note that time is specified in milliseconds).
Long answer
There are two functions that allow you to schedule a function execution.
setTimeout(func, delay) which will execute given function after given delay. This one is used for one-off execution of functions.
setInterval(func, delay) which will execute given function repeatedly after delay passes. After initial delay, specified function will be executed. Timer will then be reset and function will be executed again once delay passes once more, and so on.
Both functions can be canceled using their counterparts (clearTimeout and clearInterval).

Related

Best way to slow down javascript canvas animation?

I have a canvas animation that I wish to slow down, it's pretty simple noise effect that moves around pixels. demo can be found here: https://codepen.io/anon/pen/eyyjqm - I want to slow down the pixels movement/jitter.
const canvas = document.querySelector('canvas'),
ctx = canvas.getContext('2d')
canvas.width = canvas.height = 128
resize();
window.onresize = resize;
function resize() {
canvas.width = window.innerWidth * window.devicePixelRatio / 1
canvas.height = window.innerHeight * window.devicePixelRatio / 1
canvas.style.width = window.innerWidth + 'px'
canvas.style.height = window.innerHeight + 'px'
}
function noise(ctx) {
const w = ctx.canvas.width,
h = ctx.canvas.height,
iData = ctx.createImageData(w, h),
buffer32 = new Uint32Array(iData.data.buffer),
len = buffer32.length
let i = 1
for(; i < len;i++)
if (Math.random() < 0.5) buffer32[i] = 0xffffffff;
ctx.putImageData(iData, 0, 0);
}
(function loop() {
noise(ctx);
requestAnimationFrame(loop);
})();
I've tried;
window.setInterval('noise(ctx)',10);
but this looks so jittery and not very smooth because im setting an interval of 10 frames. What would be a better way to slow down the animation?
Appreciate any ideas! Thanks, John
Here is an approach maybe can help you.
The requestAnimationFrame pass as parameter the currentTime which is executing, so you can get some delta currentTime - oldTime time in each call and if is very short not execute the noise function again, on the other hand if it has passed a considerable time execute it again, this deltaTime can de set:
something like this:
delta = 200;
oldTime = 0;
function loop(currentTime) {
if(oldTime === 0) {
oldTime = currentTime;
}
if((currentTime - oldTime) >= delta){
noise(ctx);
oldTime = currentTime;
}
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
here is working: https://codepen.io/anon/pen/GyyYKa
I rewrote some of the code to make it slightly more efficient and to hopefully get a similar effect you were looking for:
const w = ctx.canvas.width;
h = ctx.canvas.height;
const iData = ctx.createImageData(w, h);
buffer32 = new Uint32Array(iData.data.buffer);
len = buffer32.length;
window.setInterval('noise(ctx)',38);
function noise(ctx) {
let i = 1
for(; i < len;i += 4)
if (Math.random() < 0.4)
{
buffer32[i] = 0xffffffff;
} else {
buffer32[i] = 0x00000000;
}
ctx.putImageData(iData, 0, 0);
}
//(function loop() {
//noise(ctx);
// requestAnimationFrame(loop);
//})();
I would however recommend applying a more conventional noise algorithm such as the simplex algorithm. See example here: http://haptic-data.com/toxiclibsjs/examples/simplex-noise-canvas. It will most likely be much smoother.
I've used this method in the past to limit how frequently the animation is updated.
let frame = 0
let frameLimit = 3
draw() {
// animation code here...
}
animate() {
frame++
if (frame % frameLimit === 0) {
// make some changes then redraw animation
draw()
}
requestAnimationFrame(animate)
}
Now your animation will only update every third frame. you can then easily adjust the value to speed up or slow down your animation. I don't want to claim that this is the best approach but its an alternative that I didn't see listed.

Animating a section of a path using canvas

I'm trying to animate a section of a path using canvas. Basically I have a straight line that runs through three links. When you hover over a link the section of the path behind the link should animate into a sine wave.
I have no problem animating the whole path into the shape of a sine wave but when it comes to animating a section of a path, I'm at a loss :(
I've attached a reference image below to give an idea of what it is I'm trying to achieve.
Below is jsfiddle of the code I'm currently using to animate the path. I'm a canvas noob so forgive me if it awful...
https://jsfiddle.net/9mu8xo0L/
and here is the code:
class App {
constructor() {
this.drawLine();
}
drawLine() {
this.canvas = document.getElementById('sine-wave');
this.canvas.width = 1000;
this.ctx = this.canvas.getContext("2d");
this.cpY = 0;
this.movement = 1;
this.fps = 60;
this.ctx.moveTo(0, 180);
this.ctx.lineTo(1000, 180);
this.ctx.stroke();
this.canvas.addEventListener('mouseover', this.draw.bind(this));
}
draw() {
setTimeout(() => {
if (this.cpY >= 6) return;
requestAnimationFrame(this.draw.bind(this));
// animate the control point
this.cpY += this.movement;
const increase = (90 / 180) * (Math.PI / 2);
let counter = 0;
let x = 0;
let y = 180;
this.ctx.beginPath();
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
for(let i = 0; i <= this.canvas.width; i += 6) {
this.ctx.moveTo(x,y);
x = i;
y = 180 - Math.sin(counter) * this.cpY;
counter += increase;
this.ctx.lineTo(x, y);
this.ctx.stroke();
}
}, 1000 / this.fps);
}
}
Simply split the drawing of the line in three parts, drawing 1/3 as straight line, animate the mid section and add the last 1/3.
I'll demonstrate the first 1/3 + animation and leave the last 1/3 as an exercise (also moved the stroke() outside the loop so it doesn't overdraw per segment) - there is room for refactoring and optimizations here but I have not addressed that in this example -
let x = this.canvas.width / 3; // start of part 2 (animation)
let y = 180;
this.ctx.beginPath();
this.ctx.clearRect(0, x, this.canvas.width, this.canvas.height);
// draw first 1/3
this.ctx.moveTo(0, y);
this.ctx.lineTo(x, y);
// continue with part 2
for(let i = x; i <= this.canvas.width; i += 6) {
x = i;
y = 180 - Math.sin(counter) * this.cpY;
counter += increase;
// add to 2. segment
this.ctx.lineTo(x, y);
}
// stroke line
this.ctx.stroke();
Modified fiddle

No animation, endless loop in Canvas

I am trying to create what I thought would be a simple Canvas to move a point from A to B using requestAnimationFrame.
http://jsfiddle.net/p1L81yk4/1/
Unfortunately it is not showing anything, and the animation loop seems to be headed towards infinity.
Can anyone explain what I am doing wrong? And is there a flag I can create to show when the animation should stop?
var startX = 10,
startY = 10;
var endX = 200,
endY = 350;
var speed = 1;
var dx = endX - startX;
var dy = endY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
var moves = distance / speed;
var xunits = (dx / moves);
var yunits = (dy / moves);
var posit = {
x: startX,
y: startY
};
var fps = 60;
var delay = 1000 / 30;
function draw() {
ctx.clearRect(0, 0, canv.width, canv.height);
ctx.save();
ctx.translate(startX, startY);
ctx.beginPath();
ctx.arc(0, 0, 5, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
function move() {
startX += dx;
startY += dy;
console.log(posit);
if (moves > 0) {
moves++;
posit.x += xunits;
posit.y += yunits;
}
}
var start = 0;
function animate() {
running = true;
var current = new Date().getTime(),
delta = current - start;
if (delta >= delay) {
move();
draw();
start = new Date().getTime();
}
anim = requestAnimationFrame(animate);
}
animate();
You should not use save and restore, these functions is for saving and restoring the images.
You should translate posit.x and posit.y instead of startX and startY.
You are changing startX and startY which is not reasonable.
You need a if to determinate if or if not to continue aniamtion.
Which ends up with a working code:
var canv = document.getElementById('canv'),
ctx = canv.getContext('2d');
var startX = 10,
startY = 10;
var endX = 200,
endY = 350;
var speed = 2;
var dx = endX - startX;
var dy = endY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
var moves = distance / speed;
var xunits = (dx / moves);
var yunits = (dy / moves);
var posit = {
x: startX,
y: startY
};
var fps = 60;
var delay = 1000 / 60;
function draw() {
ctx.clearRect(0, 0, canv.width, canv.height);
//ctx.save();
ctx.translate(posit.x, posit.y);
ctx.beginPath();
ctx.arc(0, 0, 5, 0, Math.PI * 2);
ctx.fill();
ctx.translate(-posit.x, -posit.y);
//ctx.restore();
}
function move() {
//startX += dx;
//startY += dy;
//console.log(posit);
if (moves > 0) {
moves++;
posit.x += xunits;
posit.y += yunits;
}
}
var start = 0;
function animate() {
running = true;
var current = new Date().getTime(),
delta = current - start;
if (delta >= delay) {
move();
draw();
start = new Date().getTime();
}
if(posit.y < endY)
anim = requestAnimationFrame(animate);
}
animate();
<canvas id="canv" width="500" height="500"></canvas>
Using basic trigonometry you can make the entire code look and read easier, with less up front variables. I still need to set quite a lot of variables here, but you would want to mostly calculate them on the fly (which means you would have to move things like angle, from and to value and distance into the myDraw() function).
var myCanvas = document.getElementById('myCanvas');
var myContext = myCanvas.getContext('2d');
myContext.fillStyle = '#000';
// Values expressed as x, y, positions
var fromValue = [300,20];
var toValue = [100,100];
// time expressed in Milliseconds
var time = 5000;
var start = Date.now();
// Get the angle with the arctangent function
// tan(angle) = opposite / adjacent => atan(opposite / adjacent) = angle
// atan2 is used because you can pass it the lengths and it takes care of
// some negative edge cases.
var angle = Math.atan2(toValue[0] - fromValue[0], toValue[1] - fromValue[1]);
// Basic distance because.. Pythagoras. (a^2 = sqrt(b^2 + c^2))
var distance = Math.sqrt(Math.pow(toValue[0] - fromValue[0], 2) + Math.pow(toValue[1] - fromValue[1], 2));
function myDraw(now){
// The max distance can be divided by the total time, multiplied by the time that has passed
var t = (distance / time * (now - start));
var x = fromValue[0] + Math.sin(angle) * t;
var y = fromValue[1] + Math.cos(angle) * t;
// Clear the canvas by resetting its width
myCanvas.width = myCanvas.width;
// Draw the arc at position x and y
myContext.arc(x, y, 3, 0, Math.PI * 2);
myContext.fill();
// Return false if the animation is done.
if(now < start + time) return true;
else return false;
}
function myAnimate(){
// Keep executing as long as myDraw() returns true
if(myDraw(Date.now())) window.requestAnimationFrame(myAnimate);
}
myAnimate();
<canvas width="500" height="500" id="myCanvas" />

Acceleration setInterval in Jquery

I am trying to create a bouncing ball (on a canvas). I would love if the ball could accelerate when going up and down. No idea how to do this with setInterval. Here is my code:
setInterval(function animate() {
ctx.clearRect( 0, 0, canvas.width, canvas.height);
if (movement1 === true) {
dotHeight += 1;
if (dotHeight >= 100) movement1 = false;
} else {
dotHeight -= 1;
if (dotHeight <= 0) movement1 = true;
}
ctx.beginPath();
ctx.arc(canvas.width / 2, (canvas.height / 2) + dotHeight, dotSize, 0, 2 * Math.PI);
ctx.fillStyle = "white";
ctx.fill();
}, 4);
This results in a linear movement. I would love to have a natural movement. Basically starting fast and getting slower when reaching the top and vice versa.
You should have both a speed and a gravity (or acceleration) variable:
speed tells you how many units (pixels here) is going to travel your object in the current update.
gravity tells you by how many units is speed increased on each update.
You want a constant gravity so that speed is increasing the same amount of pixels on each update. That will give you a variable speed, so that your object (dot here) is travelling longer or shorter distances on each update, depending on the direction it is travelling.
To make the dot bounce just change the direction of its speed once it reaches the floor. You just need to multiply it by -1 or, instead of that, you could multiply it by a bouncingFactor (-1 < bouncingFactor < 0) so that it loses energy on each bounce:
Here you can see a working example:
var canvas = document.getElementById("canvas");
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
var ctx = canvas.getContext("2d");
var frames = 0; // Frame counter just to make sure you don't crash your browser while editing code!
// DOT STUFF:
var dotSize = 20;
var dotMinY = 0 + dotSize; // Start position
var dotMaxY = canvas.height - dotSize; // Floor
var dotY = dotMinY;
var dotSpeed = 0;
var dotLastBounceSpeed = 0; // You can use this to determine whether the ball is still bouncing enough to be visible by the user.
var center = canvas.width / 2; // Try to take every operation you can out of the animate function.
var pi2 = 2 * Math.PI;
// WORLD STUFF:
var gravity = .5;
var bounceFactor = .8; // If < 1, bouncing absorbs energy so ball won't go as high as it was before.
// MAIN ANIMATION LOOP:
function animate() {
ctx.clearRect( 0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(center, dotY, dotSize, 0, pi2);
ctx.fillStyle = "red";
ctx.fill();
// First, dotSpeed += gravity is calculated and that returns the new value for dotSpeed
// then, that new value is added to dotY.
dotY += dotSpeed += gravity;
if(dotY >= dotMaxY ) {
dotY = dotMaxY;
dotSpeed *= -bounceFactor;
}
var dotCurrentBounceSpeed = Math.round(dotSpeed * 100); // Takes two decimal digits.
if(frames++ < 5000 && dotLastBounceSpeed != dotCurrentBounceSpeed) {
dotLastBounceSpeed = dotCurrentBounceSpeed;
setTimeout(animate, 16); // 1000/60 = 16.6666...
}
else alert("Animation end. Took " + frames + " frames.");
}
animate();
html, body, #canvas {
position:relative;
width: 100%;
height: 100%;
margin: 0;
overflow:hidden;
}
<canvas id="canvas"></canvas>
You should also consider using requestAnimationFrame insted of setTimeout. From the MDN doc:
The Window.requestAnimationFrame() method tells the browser that you
wish to perform an animation and requests that the browser call a
specified function to update an animation before the next repaint. The
method takes as an argument a callback to be invoked before the
repaint.
The same example with requestAnimationFrame:
var canvas = document.getElementById("canvas");
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
var ctx = canvas.getContext("2d");
var frames = 0; // Frame counter just to make sure you don't crash your browser while editing code!
// DOT STUFF:
var dotSize = 20;
var dotMinY = 0 + dotSize; // Start position
var dotMaxY = canvas.height - dotSize; // Floor
var dotY = dotMinY;
var dotSpeed = 0;
var dotLastBounceSpeed = 0; // You can use this to determine whether the ball is still bouncing enough to be visible by the user.
var center = canvas.width / 2; // Try to take every operation you can out of the animate function.
var pi2 = 2 * Math.PI;
// WORLD STUFF:
var gravity = .5;
var bounceFactor = .8; // If < 1, bouncing absorbs energy so ball won't go as high as it was before.
// MAIN ANIMATION LOOP:
function animate() {
ctx.clearRect( 0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(center, dotY, dotSize, 0, pi2);
ctx.fillStyle = "red";
ctx.fill();
// First, dotSpeed += gravity is calculated and that returns the new value for dotSpeed
// then, that new value is added to dotY.
dotY += dotSpeed += gravity;
if(dotY >= dotMaxY ) {
dotY = dotMaxY;
dotSpeed *= -bounceFactor;
}
var dotCurrentBounceSpeed = Math.round(dotSpeed * 100); // Takes two decimal digits.
if(frames++ < 5000 && dotLastBounceSpeed != dotCurrentBounceSpeed) {
dotLastBounceSpeed = dotCurrentBounceSpeed;
//setTimeout(animate, 10);
window.requestAnimationFrame(animate); // Better!!
}
else alert("Animation end. Took " + frames + " frames.");
}
animate();
html, body, #canvas {
position:relative;
width: 100%;
height: 100%;
margin: 0;
overflow:hidden;
}
<canvas id="canvas"></canvas>
As you can see, you only need to change one line of code! However, you may need a polyfill so that you fall back to setTimeout if the browser does not support requestAnimationFrame.
You can learn more about requestAnimationFrame in this post. It explains the basics and also how to set a custom frame rate.
The basic principle is to use a velocity variable as opposed to a constant height increment. So instead of dotHeight += 1 or dotHeight -= 1 you would do dotHeight += dotVelocity, where you define dotVelocity, and subtract it by a constant value (gravity) whenever the ball is in the air.
var dotHeight = 0;
var dotVelocity = 3; // start out moving up, like in your example
var gravity = .1; // you can adjust this constant for stronger/weaker gravity
setInterval(function animate() {
ctx.clearRect( 0, 0, canvas.width, canvas.height);
if (dotHeight > 0) { // if it hit the ground, stop movement
dotVelocity -= gravity;
dotHeight += dotVelocity;
}
ctx.beginPath();
ctx.arc(canvas.width / 2, (canvas.height / 2) + dotHeight, dotSize, 0, 2 * Math.PI);
ctx.fillStyle = "white";
ctx.fill();
}, 4);
You could use a speed variable instead of the constant 1 to determine how "far" to move the ball, like this:
var speed = 1;
setInterval(function animate() {
ctx.clearRect( 0, 0, canvas.width, canvas.height);
if (movement1 === true) {
dotHeight += speed;
if (dotHeight >= 100) movement1 = false;
// make it faster
speed += 1;
} else {
dotHeight -= speed;
if (dotHeight <= 0) movement1 = true;
// slow down
speed -= 1;
}
ctx.beginPath();
ctx.arc(canvas.width / 2, (canvas.height / 2) + dotHeight, dotSize, 0, 2 * Math.PI);
ctx.fillStyle = "white";
ctx.fill();
}, 4);

restart html5 animation in a given interval

I have a html5 canvas in which something is animated within a given time. what i'd like to do is repeat this code ("reset and restart" the drawing) in a certain interval.
var canvas = $("#paper")[0];
var c = canvas.getContext("2d");
var startX = 50;
var startY = 50;
var endX = 100;
var endY = 100;
var amount = 0;
setInterval(function() {
amount += 0.05; // change to alter duration
if (amount > 1) amount = 1;
c.clearRect(0, 0, canvas.width, canvas.height);
c.strokeStyle = "black";
c.moveTo(startX, startY);
// lerp : a + (b - a) * f
c.lineTo(startX + (endX - startX) * amount,
startY + (endY - startY) * amount);
c.stroke();
}, 30);​
Assign the setInterval function to a variable. Then you can clear it
var interval = setInterval(function () {});
clearInterval(interval);
In you case (function calle setup) you could use:
var interval = setInterval(setup, 30);
And when you want to clear it call:
clearInterval(interval).

Categories

Resources