Ps: The picture drops a little

Is the physics animation library really “physics”?

Android’s support-dynamic-Animation library has been around for quite some time. Those of you who have used FlingAnimation know that you can set maximum and minimum values for an animation.

Yi! Something seems to be wrong!!

When it reaches a maximum or a minimum, the velocity returns to zero. This is not the physical experience I was looking for

Looking for a reason ~

FlingAnimation. Java this class source code is not much. Well, let’s take our time to find out where it happened…

@Override
boolean updateValueAndVelocity(long deltaT) {

    MassState state = mFlingForce.updateValueAndVelocity(mValue, mVelocity, deltaT);
    mValue = state.mValue;
    mVelocity = state.mVelocity;

    // When the animation hits the max/min value, consider animation done.
    if (mValue < mMinValue) {
        mValue = mMinValue;
        return true;
    }
    if (mValue > mMaxValue) {
        mValue = mMaxValue;
        return true;
    }

    if (isAtEquilibrium(mValue, mVelocity)) {
        return true;
    }
    return false;
}
Copy the code

isAtEquilibrium(float value, float velocity):

@Override
boolean isAtEquilibrium(float value, float velocity) {
    return value >= mMaxValue
            || value <= mMinValue
            || mFlingForce.isAtEquilibrium(value, velocity);
}
Copy the code

MFlingForce. IsAtEquilibrium (float value, float velocity) :

    @Override
    public boolean isAtEquilibrium(float value, float velocity) {
        return Math.abs(velocity) < mVelocityThreshold;
    }
Copy the code

It can be seen that when the latest value is less than the minimum value or greater than the maximum value, it is directly set to the corresponding threshold value and the animation is forced to end. The normal end of the animation should be when the absolute value of the speed is less than the speed threshold.

To modify the

You all remember from your books about collisions of forces (ideal collisions)

@Override
boolean updateValueAndVelocity(long deltaT) {
    MassState state = mFlingForce.updateValueAndVelocity(mValue, mVelocity, deltaT);
    mValue = state.mValue;
    mVelocity = state.mVelocity;

    if (mValue < mMinValue) {
        mValue = 2 * mMinValue - mValue;
        mVelocity = -mVelocity;
    } else if (mValue > mMaxValue) {
        mValue = 2 * mMaxValue - mValue;
        mVelocity = -mVelocity;
    }

    return isAtEquilibrium(mValue, mVelocity);
}
Copy the code

The modified result is shown as follows:

It’s not very “physical”, but at least it’s a bit physical

At the ideal point friction is 0##

The original FlingAnimation does not support:

Public FlingAnimation setFriction(@floatrange (from = 0.0, fromInclusive = false) float friction) { if (friction <= 0) { throw new IllegalArgumentException("Friction must be positive"); } mFlingForce.setFrictionScalar(friction); return this; }Copy the code

You can see that an error is thrown directly when set to 0The latest value and speed can also be updated with the denominator 0:

    MassState updateValueAndVelocity(float value, float velocity, long deltaT) {
        mMassState.mVelocity = (float) (velocity * Math.exp((deltaT / 1000f) * mFriction));
        mMassState.mValue = (float) (value - velocity / mFriction
                + velocity / mFriction * Math.exp(mFriction * deltaT / 1000f));
        if (isAtEquilibrium(mMassState.mValue, mMassState.mVelocity)) {
            mMassState.mVelocity = 0f;
        }
        return mMassState;
    }
Copy the code

Hum! I just want friction to be zero

You probably know that friction is zero, which means constant motion, no loss of energy, and no loss of speed (saving a lot of calculations).

Let’s fix it:

setFriction(float friction)

Public FlingBoundAnimation setFriction(@floatrange (from = 0.0) float friction) {if (friction < 0) {throw new IllegalArgumentException("Friction must be positive"); } mFlingForce.setFrictionScalar(friction); return this; }Copy the code

updateValueAndVelocity(float value, float velocity, long deltaT)

MassState updateValueAndVelocity(float value, float velocity, long deltaT) { if (mFriction ! = 0) { mMassState.mVelocity = (float) (velocity * Math.exp((deltaT / 1000f) * mFriction)); mMassState.mValue = (float) (value - velocity / mFriction + velocity / mFriction * Math.exp(mFriction * deltaT / 1000f)); if (isAtEquilibrium(mMassState.mValue, mMassState.mVelocity)) { mMassState.mVelocity = 0f; } } else { mMassState.mVelocity = velocity; mMassState.mValue = value + velocity * (deltaT / 1000f); } return mMassState; }Copy the code

updateValueAndVelocity(long deltaT)

@Override boolean updateValueAndVelocity(long deltaT) { MassState state = mFlingForce.updateValueAndVelocity(mValue, mVelocity, deltaT); mValue = state.mValue; mVelocity = state.mVelocity; if (mValue < mMinValue) { mValue = 2 * mMinValue - mValue; mVelocity = -mVelocity; } else if (mValue > mMaxValue) { mValue = 2 * mMaxValue - mValue; mVelocity = -mVelocity; } return mFlingForce.mFriction ! = 0 && isAtEquilibrium(mValue, mVelocity); }Copy the code

The results are as follows:

Modified successfully

UpdateValueAndVelocity (Long deltaT)