-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add a "dampingRatio" config prop #678
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Note that while you are at it, you can also tune things based on natural frequency rather than mass and stiffness. In this way, I suspect you’d have much more intuitive props (natural frequency and damping ratio). A few more comments. You are probably too far along to deal with these, but:
The library is very cool and I don’t really see any fundamental issues. These comments are really just to avoid confusion in terminology and suggestions for properties that might be more intuitive for users. |
@mtiller Is there any prior art that implements these concepts? What algorithm would you recommend? Let's say we want This is the current JavaScript implementation: https://github.com/react-spring/react-spring/blob/85d5fea07e3877a8873c14d305df2b246d705596/src/animated/FrameLoop.ts#L76-L109 Someone wrote a Rust version (except it uses RK4 physics): https://gist.github.com/aleclarson/dc2064b80d1615e19a75dcbbc0ed5f31 |
@aleclarson So the basic skeleton here is reasonably sound. Let me point out a few things though. Being pedantic, I'd write it more like this: const dt = 1; /** unit: milliseconds */
const numSteps = Math.floor(time - lastTime)/dt;
for (let i = 0; i < numSteps; ++i) {
let springForce = -config.stiffness * (position - to);
let dampingForce = -config.damping * velocity;
let acceleration = (springForce + dampingForce) / config.mass
velocity = velocity + (acceleration * 1) *dt /1000;
position = position + (velocity * 1) *dt/1000; The key points are that you are implicitly using a "time step" of 1 millisecond. This isn't very clear from the way it is written, so I tried to make that more explicit. The nice thing here is that you could actually change it (by changing the value of Now the important point is that you can keep the code the way it is if you want. Even if you adopt the changes I suggest, I'm really just renaming things and making stuff more explicit. The calculations should yield precisely the same results. So now let me get to your essential question...how to represent this damping ratio and natural frequency stuff? How do you need to change those lines of code to handle that? The answers is you don't need to change that code. Instead, what you need is to have a (potentially optional) way of computing config.tension and config.friction. You see, the damping ratio is just Regarding solver: It isn't correct to say "RK4 physics". RK4 Runge-Kutta 4-order is a solution method. It is a way of taking the continuous mathematical relations (which are derived from the physics) and producing a solution that is represented as a sequence of numbers. What you are doing is a simple forward Euler solution method which is intuitive but has serious stability issue for "real" systems. In your case, this is a simple second order system so I don't think you'll run into any problems assuming your time steps are sufficiently small. On that note, I should point out that you are currently using 1 millisecond time steps. But I suspect that is much finer than necessary. According to this (page 23, equation 23) you could probably get by with setting Clamping: Hopefully, using a dampingRatio of 1 will help with your "clamping" concerns here. I presume these exist in cases where parameters have a limited domain (e.g., opacity being 0-1). In that case, I would suggest you get rid of the concept of clamping and actually implement what we would call a "hard stop" or "mechanical limit". It is actually quite easy to do, physical and fun. Let me explain. Let's say you go past the min or max value. What you can do is simulate the behavior of a bouncing ball (shameless plug). What that means is that whenever you hit the limit, you set the position to the limit value (min or max, depending on which way you are going) but you reverse the value of the velocity and multiple it by what is called the "coefficient of restitution". This will trigger a "bounce" away from the mechanical limit. In this way, you never get a position value that is outside the domain you want. But what if you want the "clamp" behavior? Easy...make the coefficient of restitution zero. Then you get a perfectly plastic collision which means the object just stops where it is. As long as your |
react-spring initially had animated's approach, rk4 and the same configs, but i decided to go for react-motions configs and also exchanged rk4 for what seemed to me could be a little simpler impl (semi explicit euler i believe). as for the configs, i figured, react-motion is the biggest animation library in react, so i wanted users to get a familiar feel. @mtiller going forward im not sure if changing everything would be a good way. i would suggest if we do it we add it incrementally. this is also how animated rolls, they always offered both ways, see: https://github.com/animatedjs/animated/blob/master/src/SpringConfig.js some links that were of importance back then: https://web.archive.org/web/20170606145659/http://gafferongames.com/game-physics/integration-basics/ https://web.archive.org/web/20160101081158/http://gafferongames.com/game-physics/fix-your-timestep/ i'm not versed in this stuff at all, i just care about ergonomics and most importantly speed. super high physics accuracy isn't that important, but even if an implementation is a split second faster, i'd take it. it would be awesome if you could take a look and see if we can improve especially performance. |
Adding these slides to the conversation which make a good job at explaining the different methods for beginners like me! http://box2d.org/files/GDC2015/ErinCatto_NumericalMethods.pdf |
- Calculate velocity for "decay" and "easing" animations - When "config.clamp" is a number, it becomes the coefficient of restitution - Account for dropped frames in "decay" and "easing" animations - Fixed "config.velocity" to be per ms instead of per sec - Implement variable timesteps as described in pmndrs#678 - Bounce animations can be done with "config.clamp" > 0
- Calculate velocity for "decay" and "easing" animations - When "config.clamp" is a number, it becomes the coefficient of restitution - Account for dropped frames in "decay" and "easing" animations - Fixed "config.velocity" to be per ms instead of per sec - Implement variable timesteps as described in pmndrs#678 - Bounce animations can be done with "config.clamp" > 0
- Calculate velocity for "decay" and "easing" animations - When "config.clamp" is a number, it becomes the coefficient of restitution - Account for dropped frames in "decay" and "easing" animations - Fixed "config.velocity" to be per ms instead of per sec - Implement variable timesteps as described in pmndrs#678 - Bounce animations can be done with "config.clamp" > 0
- Calculate velocity for "decay" and "easing" animations - When "config.clamp" is a number, it becomes the coefficient of restitution - Account for dropped frames in "decay" and "easing" animations - Fixed "config.velocity" to be per ms instead of per sec - Implement variable timesteps as described in pmndrs#678 - Bounce animations can be done with "config.clamp" > 0
- Calculate velocity for "decay" and "easing" animations - When "config.clamp" is a number, it becomes the coefficient of restitution - Account for dropped frames in "decay" and "easing" animations - Fixed "config.velocity" to be per ms instead of per sec - Implement variable timesteps as described in pmndrs#678 - Bounce animations can be done with "config.clamp" > 0 - The default value of "config.precision" is now derived from the distance between "to" and "from"
- Calculate velocity for "decay" and "easing" animations - When "config.clamp" is a number, it becomes the coefficient of restitution - Account for dropped frames in "decay" and "easing" animations - Fixed "config.velocity" to be per ms instead of per sec - Implement variable timesteps as described in #678 - Bounce animations can be done with "config.clamp" > 0 - The default value of "config.precision" is now derived from the distance between "to" and "from"
- Calculate velocity for "decay" and "easing" animations - When "config.clamp" is a number, it becomes the coefficient of restitution - Account for dropped frames in "decay" and "easing" animations - Fixed "config.velocity" to be per ms instead of per sec - Implement variable timesteps as described in #678 - Bounce animations can be done with "config.clamp" > 0 - The default value of "config.precision" is now derived from the distance between "to" and "from"
Added in #808
|
- Calculate velocity for "decay" and "easing" animations - When "config.clamp" is a number, it becomes the coefficient of restitution - Account for dropped frames in "decay" and "easing" animations - Fixed "config.velocity" to be per ms instead of per sec - Implement variable timesteps as described in #678 - Bounce animations can be done with "config.clamp" > 0 - The default value of "config.precision" is now derived from the distance between "to" and "from"
Uh oh!
There was an error while loading. Please reload this page.
🚀 Feature Proposal
While at React Europe, I talked to @mtiller about how "damping ratio" is not the same as "friction" and, in fact, the concept of a damping ratio can be easier for users to understand. When the damping ratio is less than 1, it guarantees that your spring animation will never overshoot its target value. Likewise, when above 1, it guarantees overshooting will occur.
Note: I'm not suggesting we should remove
friction
, but you wouldn't be able to specify bothdampingRatio
andfriction
for the same animation.Motivation
Make it easier to control whether or not overshooting occurs, without having to resort to using the
clamp
config prop.Related: https://twitter.com/mtiller/status/1131676374840815617
The text was updated successfully, but these errors were encountered: