How to Optimize js animation with requestAnimationFrame and introduce delta? - javascript

I have a basic animation of a ball falling with a gradually increasing velocity.
DEMO
It is somewhat working as expected with an expected velocity initially. So on click on the canvas we can repeat the animation , so each time we can see the ball speed keeps increasing. After 5-6 click the ball is moving with high velocity.
So is the issue is with frame rate , and expected unit move per frame. ?
1. How to reduce/optimize the loop call ? , and get a constant velocity in each click.
I wont prefer to change the value inside Ball model, but the time of ball.update() must be reduced.
var ball = new Ball(10,10,20);
function animate() {
ctx.fillStyle = "#CCC";
ctx.fillStyle = "#CCC";
ctx.fillRect(0, 0, 500, 500);
ball.update();
ball.draw(ctx);
requestAnimationFrame(animate); // add timestamp to optimize ?
}
and on click the reset animation is like this
$("#element").on('click', function () {
ball = new Ball(10,10,20)
requestAnimationFrame(animate);
});
and 2. Why it is working with expected speed initialy ? and I prefer to clear the animation after the ball moves out of canvas.

The main reason is that you are combining setTimeout with requestAnimationFrame which kind of defeat its purpose.
Not only are your rAF triggered by an inaccurate timer but when rAF kicks in it is not sure you will get a frame for that round which can produce jerky result.
Change these lines:
setTimeout(function () {
animationId = requestAnimationFrame(animate);
}, 1000/60);
to
//setTimeout(function () {
animationId = requestAnimationFrame(animate);
//}, 1000/60);
Then reset your ball as with Greg's answer. There is no need to call rAF again as it is already running:
$(function(){
requestAnimationFrame(animate);
$("#element").on('click', function () {
ball = new Ball(10,10,20);
});
});
Modified fiddle
You could also reset the ball by updating its properties.

Here's a fork of your fiddle that works: http://jsfiddle.net/8egrw/
Comment out this line:
$("#element").on('click', function () {
ball = new Ball(10,10,20)
//animationId = requestAnimationFrame(animate);
});

Related

Why does camera Y position change sporadically with PointerLockControls in Three.js?

I'm currently working on a first person 3D game in javascript using Three.js. I'm using PointerLockControls to control camera movement, and for some reason, the camera seems to be jumping around randomly on the Y axis. It's not noticable in the game itself but if I print out camera.position in my render loop, it changes sporadically. I'm using WASD to call controls.moveForward() and controls.moveRight() which shouldn't affect Y position (I think, correct me if I'm wrong) but it changes nonetheless. This is the code of interest:
var controls = new THREE.PointerLockControls(camera, document.body);
document.onclick = function () {
controls.lock();
}
function loop() {
if (wDown) {
controls.moveForward(0.1);
}
if (sDown) {
controls.moveForward(-0.1);
}
if (dDown) {
controls.moveRight(0.1);
}
if (aDown) {
controls.moveRight(-0.1);
}
// printing out camera position
console.log(camera.position);
renderer.render(scene, camera);
requestAnimationFrame(loop);
}
Edit: I should note, the Y position only changes when I move using WASD.

Extreme Novice needing assistance with mousePressed() event

I am VERY new to P5.js/processing (taking programming for artists). I am trying to make a crude game where an image (Jar Jar) bounces across the screen and another image (lightsaber) that moves with the mouse and when the mouse attached image goes over the bouncing image then the lightsaber will be mirrored and activate a sound. If this at all makes sense...
I have the bouncing image part down so far, but I am unable to make the mousePressed() function work. like I mentioned, I need the "lightsaber.png" to flip when the mouse is pressed. Also, when the mouse is pressed and is directly over the JarJar image, how would I add a score count and sound event?
Thank you!
here is my code so far:
let jarJar;
let jarJarX=5;
let jarJarY=5;
let xspeed;
let yspeed;
let lightSaber;
function preload() {
jarJar = loadImage('jarjar.png');
lightSaber= loadImage ('lightSaber.png');
}
function setup() {
createCanvas(700,700);
xspeed=random (15,22);
yspeed=random (15,22);
}
function draw() {
background(0);
image (lightSaber,mouseX,mouseY,100,100);
image(jarJar,jarJarX,jarJarY, 140, 200);
jarJarX= jarJarX+xspeed;
if (jarJarX<=-300|| jarJarX>=width+200){
xspeed=xspeed*-1;
}
jarJarY= jarJarY+yspeed;
if (jarJarY<-200|| jarJarY>=height+200 ){
yspeed=yspeed*-1;
}
//picture mirrors when mouse pressed
if mouseClicked(){
scale(-1,1);
image(lightSaber);
}
//score counter coordinate with lightsaber hitting image
//
}
Let it be known that I'm not proficient at javaScript. This said, your question is quite simple so I can help anyway.
Some framework will have simple ways to mirror images. Processing likes to scale with a negative number. I re-coded some of your stuff to accommodate my changes. The main changes goes as follows:
I added a method to draw the lightsaber so we can "animate" it (read: flip it for a couple frames when the user clicks around).
I added a 'score' global variable to track the score, and a way for the user to see that score with the text method.
I added a method called "intersect" which isn't very well coded as it's something I did back when I was a student (please don't hurt me, it works just right so I still use it from time to time). For more details on how simple collisions works, take some time to read this answer I wrote some time ago, there are nice pictures too!
I added a mouseClicked method. This method will act like an event, which means that it will be triggered by a specific call (a left mouse button click in this case). This method contains the code to check for a collision between the squares which are the images. If there's an overlap, the score will increase and jarjar will run in another direction (this part is a bonus to demonstrate that this is the place where you can get creative about the collision).
I commented the code so you can get what I'm doing more easily:
let jarJar;
let jarJarX=5;
let jarJarY=5;
let xspeed;
let yspeed;
let lightSaber;
let flipLength;
let score = 0;
function preload() {
jarJar = loadImage('jarjar.png');
lightSaber= loadImage ('lightSaber.png');
}
function setup() {
createCanvas(700, 700);
runJarJarRun();
}
function draw() {
background(0);
drawLightSaber(); // this way I can deal with the lightsaber's appearance in a dedicated method
image(jarJar, jarJarX, jarJarY, 140, 200);
jarJarX= jarJarX+xspeed;
if (jarJarX<=-300|| jarJarX>=width+200) {
xspeed=xspeed*-1;
}
jarJarY= jarJarY+yspeed;
if (jarJarY<-200|| jarJarY>=height+200 ) {
yspeed=yspeed*-1;
}
//score counter coordinate with lightsaber hitting image
textSize(30);
fill(200, 200, 0);
text('Score: ' + score, 10, 40);
}
function drawLightSaber() {
if (flipLength) { // if the number is > 0 this will be true
flipLength--; // measure how ling the saber is flipped in frames # ~60 frames per second
push(); // isolating the translate ans scale manpulations to avoid ruining the rest of the sketch
translate(mouseX + 100, 0); // makes the coordinates so once flipped the lightsaber will still appear at the same location
scale(-1.0, 1.0); // flip x-axis backwards
image (lightSaber, 0, mouseY, 100, 100);
pop(); // ends the sequence started with 'push();'
} else {
image (lightSaber, mouseX, mouseY, 100, 100);
}
}
function runJarJarRun() {
xspeed=random (5, 10);
yspeed=random (5, 10);
}
function mouseClicked() { // this method will trigger once when the left mouse button is clicked
flipLength = 10;
if (intersect(jarJarX, jarJarY, 140, 200, mouseX, mouseY, 100, 100)) {
score++;
runJarJarRun(); // as a bonus, jarjar will run in another direction on hit
// you could totally put some more special effects, like a flash, a sound, some 'mesa ouchie bad!' text, whatever speaks to you
}
}
function intersect(x1, y1, w1, h1, x2, y2, w2, h2) {
let checkX = false;
let checkY = false;
if ( (x1<x2 && (x1+w1)>x2) || (x1<(x2+w2) && (x1+w1)>x2+w2) || (x1>x2 && (x1+w1)<(x2+w2)) ) {
checkX = true;
}
if ( (y1<y2 && (y1+h1)>y2) || (y1<(y2+h2) && (y1+h1)>y2+h2) || (y1>y2 && (y1+h1)<(y2+h2)) ) {
checkY = true;
}
return (checkX && checkY);
}
If there's something you don't understand, let me know in a comment and I'll be happy to elaborate. Good luck and have fun!
Hi and welcome to stack overflow. One thing to keep in mind when submitting here (or any forum where you're looking for help with code) is to post a minimal reproducible example. You'll be much more likely to get useful responses.
You'll also want to separate out your questions, as they each have multi-step responses.
Your first question is about how to get your sketch to display something when you press the mouse down. Your syntax isn't quite correct there. Here's a minimal example of how to check for a mouse held down.
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
if (mouseIsPressed == true) {
ellipse(100, 100, 100, 100);
}
}
Just a quick note that I tried to make this as 'novice-friendly' as possible. The == true is optional and not usually included.

fabricjs - creating endless animation loop without lags

I am trying to create an endless animation loop of rotation with FabricJS. It works like this: the rotate function gets animation details (object to animate, duration of one animation loop, starting angle of rotation) as parameters, and invokes inner function rotateInner to launch the animation itself. rotateInner rotates given object using fabric.util.animate with linear easing function (to make rotation speed constant) to run one animation loop and to invoke itself after the loop ends, which results in an infinite animation loop.
The problem is that rotateInner lags every time between animation loops when it invokes itself through fabric.util.animate.
For example, I use animation loop with duration = 1 second and rotation angle = 180 degrees. In that case, animation makes lag every 1 second after rotating by 180 degrees.
I've found a solution to make lag occur N times less. Instead of rotating by 180 degrees for 1 second, I rotate object by 180*N degrees for N seconds. If I pick enough big N, the user will meet lag potentially never. But only potentially.
What are reasons of lags and is it possible to remove lags between animation loops completely? Or, perhaps, I went wrong way, and I should use something completely different to create endless animation loop?
Here's example which demonstrates lag (on jsfiddle). (The rotate function has additional argument - multiplier. It's N I've just wrote about above) Left rectangle rotates 180 degrees per 800 milliseconds, and the right rectangle rotates 180*10 degrees per 800*10 milliseconds, and thus the right one rotates quicker than the left one.
And, if you don't want to use jsfiddle or if example became inaccessible for some reason, here's the example itself in code:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.4.0/fabric.min.js"></script>
<style>canvas {border: 5px dotted black;}</style>
<title>FabricJS animation loop lag</title>
</head>
<body>
<canvas id="mycanvas" width="700" height="300"></canvas>
<script>
var canvas = new fabric.StaticCanvas('mycanvas');
var rectCreationOptions = {
left: canvas.getWidth()/4,
top: canvas.getHeight()/2,
fill: '#0bb',
width: canvas.getWidth()/5,
height: canvas.getHeight()/5,
originX: 'center',
originY: 'center'
};
// creating left rectangle:
var rect1 = new fabric.Rect(rectCreationOptions);
rectCreationOptions.left *= 3;
// creating right rectangle:
var rect2 = new fabric.Rect(rectCreationOptions);
canvas.add(rect1);
canvas.add(rect2);
function rotate(animatedObject, duration = 1000, multiplier = 1, startAngle = 0) {
duration *= multiplier;
var addAngle = 180 * multiplier;
(function rotateInner(startAngle) {
fabric.util.animate({
startValue: startAngle,
endValue: startAngle+addAngle,
duration: duration,
// linear easing function:
easing: function(t, b, c, d) { return c*t/d + b; },
onChange: function (value) {
animatedObject.angle = value;
canvas.renderAll();
},
onComplete: function() {
rotateInner(startAngle+addAngle);
}
});
})(startAngle);
}
rotate(rect1, 800, 1); // lags every 800 ms
rotate(rect2, 800, 10); // lags every 800*10 ms
</script>
</body>
</html>
I will be thankful for help.
Please, don't throw rotten tomatoes at me if I made tiny mistake. This is my first question on stackoverflow. :)
Best wishes everyone.

Idle animation camera Aframe

I'm using aframe 0.8.2 and I'm trying to add a camera idle animation (only for the rotation component). I've done this so far :
HTML:
<!-- Camera-->
<a-entity id = "my_c" position="8.435 0 -3.579" > <a-camera></a-camera></a-entity>
Javascript :
var scene = document.getElementById("my_s");
var camera = document.getElementById("my_c");
var anime_1 = document.createElement("a-animation");
/* Add a time of "not moving" */
var t;
window.onload = resetTimer;
// DOM Events
document.onkeypress = resetTimer;
document.onmousedown = resetTimer;
function standby() {
console.log("Start standby.");
anime_1.setAttribute("attribute","rotation");
console.log(camera.getAttribute("rotation"));
anime_1.setAttribute("dur", "80000");
anime_1.setAttribute("to", "0 360 0");
anime_1.setAttribute("easing", "linear");
anime_1.setAttribute("repeat", "indefinite");
camera.appendChild(anime_1);
}
function resetTimer() {
clearTimeout(t);
t = setTimeout(standby, 3000);
camera.removeChild(anime_1);
// 1000 milisec = 1 sec
}
The problem is, I think that my camera does not upload its position and rotation : when I do rotate my camera, the camera idle animation does not start at the same position.
Thanks for your support :)
You could handle the from and to properties differently:
...
let initCamRotation = camera.getAttribute("rotation")
let endCamRotation = initCameraRotation
endCamRotation.y += 360
anime_1.setAttribute("from", initCamRotation);
anime_1.setAttribute("to", endCamRotation);
....
A bit offtop, but the animation components animation can be paused when given pauseEvents, i'd try using it instead of the a-animation entity. Here im not sure if you can interrupt the animation while it started.

setTimeout() not working in animation function | Animation not working

I'm trying to build a very simple animation function. I'm using this tutorial to build my project:
https://www.youtube.com/watch?v=hUCT4b4wa-8
The result after the button is clicked should be a green box moving across the page from left to right. When the button is clicked, nothing happens and I don't get any console errors.
Here's my fiddle:
https://jsfiddle.net/xkhpmrtu/7/
And here's a snippet of my code:
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border: 1px solid #666;
}
</style>
<script type="application/javascript" language="javascript">
function anim(x,y) {
var canvas = document.getElementById('canvas');//reference to canvas element on page
var ctx = canvas.getContext('2d');//establish a 2d context for the canvas element
ctx.save();//save canvas state if required (not required for the tutoriral anaimation, but doesn't hurt the script so it stays for now)
ctx.clearRect(0, 0, 550, 400);//clears the canvas for redrawing the scene.
ctx.fillStyle = "rgba(0,200,0,1)";//coloring the rectangle
ctx.fillRect = (x, 20, 50, 50);//drawing the rectangle
ctx.restore();//this restores the canvas to it's original state when we saved it on (at the time) line 18
x += 5; //increment the x position by some numeric value
var loopTimer = setTimeout('draw('+x+','+y+')', 2000);// setTimeout is a function that
</script>
</head>
<body>
<button onclick="animate(0,0)">Draw</button>
<canvas id="canvas" width="550" height="400"></canvas>
</body>
Any idea what I'm doing wrong?
I just had a look at the tutorial link. I will give if a major thumbs down as it demonstrates how not to animate and how not to do many other things in Javascript.
First the script tag and what is wrong with it
// type and language default to the correct setting for javascrip
// <script type="application/javascript" language="javascript">
<script>
function anim(x,y) {
// get the canvas once. Getting the canvas for each frame of an
// animation will slow everything down. Same for ctx though will not
// create as much of a slowdown it is not needed for each frame
// var canvas = document.getElementById('canvas');
// var ctx = canvas.getContext('2d');
// Dont use save unless you have to. It is not ok to add it if not needed
// ctx.save();
// dont use literal values, canvas may change size
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "rgba(0,200,0,1)";
// this line is wrong should be ctx.fillRect(x, 20, 50, 50). It is correct in the video
ctx.fillRect = (x, 20, 50, 50);//drawing the rectangle
// restore not needed
//ctx.restore();
x += 5; //increment the x position by some numeric value
// creating a string for a timer is bad. It invokes the parser and is slooowwwwww...
// For animations you should avoid setTimeout altogether and use
// requestAnimationFrame
// var loopTimer = setTimeout('draw('+x+','+y+')', 2000);
requestAnimationFrame(draw);
// you were missing the closing curly.
}
</script>
There is lots more wrong with the tut. It can be excused due to it being near 5 years old. You should look for more up todate tutorials as 5 years is forever in computer technology.
Here is how to do it correctly.
// This script should be at the bottom of the page just befor the closing body tag
// If not you need to use the onload event to start the script.
// define a function that starts the animation
function startAnimation() {
animating = true; // flag we are now animating
x = 10;
y = 10;
// animation will start at next frame or restart at next frame if already running
}
// define the animation function
function anim() {
if (animating) { // only draw if animating
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "red"; //coloring the rectangle
ctx.fillRect(x, y, 50, 50); //drawing the rectangle
x += xSpeed;
}
// set animation timer for next frame
requestAnimationFrame(anim);
}
// add a click listener to the start button. It calls the supplied function every time you click the button
startAnimButton.addEventListener("click", startAnimation);
const ctx = canvas.getContext('2d'); // get the 2d rendering context
// set up global variables to do the animation
var x, y, animating;
animating = false; // flag we are not animating
const xSpeed = 50 / 60; // Speed is 50 pixels per second at 60fps
// dont slow the animation down via frame rate
// slow it down by reducing speed.
// You only slow frame rate if the machine
// can not handle the load.
// start the animation loop
requestAnimationFrame(anim);
canvas {
border: 1px solid #666;
}
<!-- don't add events inline -->
<button id="startAnimButton">Draw</button>
<canvas id="canvas" width="512" height="128"></canvas>

Categories

Resources