The task is to animate number of falling rectangles simulating rain, when rectangle – jet touches ground -draw star. To impelemnt it there are 3 classes: GitHub repository.
// Shapes:
class WaterJet { // falling rectangles
class RandomStar { // stars
//Each shape has
isAlive = true; // variable defines if shape still need o be rendered
draw(delta_ms) // method to draw shape obaining time from last draw
and engine to animate all of the shapes collected in array animatedItems
with requestAnimationFrame
:
class AnimateBatch {
constructor() {
this.animatedItems = []; // Container to store entities to draw )
this.delta_ms = 0; // Delta between draw() methods run
}
//Method to animate batch of shapes
#animate(previousTime) {
const currentTime = performance.now();
this.delta_ms = previousTime ? currentTime - previousTime : 0;
// Draw and filter only "alive" shapes
this.animatedItems = this.animatedItems.filter((item) => {
item.draw(this.delta_ms);
return item.isAlive; // leave only shapes with isAlive=true
});
if (this.animatedItems.length > 0) {
this.requestId = requestAnimationFrame((newTime) => this.#animate(newTime));
Speed of shapes animation is caculated in draw() method on the base of delta_ms
(time left from last draw) ,
I need to catch moment when one shape is expired and on this event add new shape to AnimateBatch.animatedItems:
function addStar(){
animateBatch.add(newStar);
}
here comes trouble, when this shape added by listener of the button it is ok:
buttonDo.addEventListener('click', function() {
addStar();
});
Cyclic printot of content of rendered shapes show that stars added and atay in array:
0. AnimatedItem-1 : true
1. WaterJet-1 : true
2. Anisothermal.html:250 ______________star added RandomStar-1
0. AnimatedItem-1 : true
1. WaterJet-1 : true
2. RandomStar-1 : true <<----added
0. AnimatedItem-1 : true
1. WaterJet-1 : true
2. RandomStar-1 : true <<---- and remains
But when the same function implemnted from listener:
jets[i].OnJetOutOfBorder( () => {
addStar();
});
In listener of the shape:
class RandomStar {
constructor(ctx, centerX, centerY, spikeLength = 2) {
this.OnStarGone = null;
}
draw(delta_ms) {
if(!this.isAlive) { // When shape already finish life: execute listener
// Implement litener when star is expired
if(this.OnStarGone)this.OnStarGone();
return;
}
addOnStarGone(callback){
this.OnStarGone = callback;
}
Than shape added to array but disappears from it :
0. AnimatedItem-1 : true
1. WaterJet-1 : true
0. AnimatedItem-1 : true
1. WaterJet-1 : true
2. Anisothermal.html:228 _____________________________listener________
0. AnimatedItem-1 : true
1. WaterJet-1 : true
2. RandomStar-5 : true <<-- it is added
2. Anisothermal.html:250 ______________star added RandomStar-5
0. AnimatedItem-1 : true
1. WaterJet-1 : true
0. AnimatedItem-1 : true <<---- And not in array any more
1. WaterJet-1 : true
0. AnimatedItem-1 : true
1. WaterJet-1 : true
The result is major letdown and I doubt if my approach to animate is right? Please help to understand such wierd bihavour
How possible to modify list of rendering shapes during the animation?
Why despite calculation of the speeds done on the base of time periods the animation goes with jerks, espatially sharp and intrrupting when a user intracts with interface (it has some other controllers to modify animation in fly). At the same time: calculation based on time difference between executing requestAnimationFrame()
gives huge error.
I see that requestAnimationFrame()
provides relatively hight FPS 16-17ms per frame, is it possble to set it lower to sacrifice update frequency in the sake of the performance?
Build seamless animation with possibility to add and remove new shapes to render list while animation.
With respect to 2 on your list of questions: The way you are doing time is wrong. requestAnimationFrame() passes the current time into the callback function, not the previous time. You call it newTime in one place, but then call previousTime and use it as a previous time in your callback. So...
Add a previousTime property in your constructor
this.previousTime = performance.now()
Change the parameter name in your callback to currentTime
animate(currentTime) {
Finally, change your delta_ms calculation appropriately
this.delta_ms = currentTime - this.previousTime
this.previousTime = currentTime