Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 22a9f9c8 authored by George Mount's avatar George Mount
Browse files

Linearly complete overscroll stretch when close

Fixes: 190475978

Damped spring animations asymptotically approach their destinations.
The final pixels can take many frames to complete, but stopping them
means that we get a disruptive jump in those final pixels.

This CL detects when the edge effect animation is close to the finish and
completes the animation with a linear velocity that matches a
common velocity at 8 pixels distant. This means that the animation
terminates quicker so that touch events directed at the contents
after the animation completes (e.g. taps) can interact with the contents
soon.

The CL also adjusts the delta for detecting zero in the stretch
animation as it was jumping by a pixel or more at the end of an otherwise
smooth animation.

Test: manual testing for the visual effect
Change-Id: Ie249b0265c5c5939b597668d5afe4f76d0430821
parent ccc98b8d
Loading
Loading
Loading
Loading
+79 −43
Original line number Diff line number Diff line
@@ -97,12 +97,28 @@ public class EdgeEffect {
     */
    private static final double VELOCITY_THRESHOLD = 0.01;

    /**
     * The speed at which we should start linearly interpolating to the destination.
     * When using a spring, as it gets closer to the destination, the speed drops off exponentially.
     * Instead of landing very slowly, a better experience is achieved if the final
     * destination is arrived at quicker.
     */
    private static final float LINEAR_VELOCITY_TAKE_OVER = 200f;

    /**
     * The value threshold before the spring animation is considered close enough to
     * the destination to be settled. This should be around 0.01 pixel.
     */
    private static final double VALUE_THRESHOLD = 0.001;

    /**
     * The maximum distance at which we should start linearly interpolating to the destination.
     * When using a spring, as it gets closer to the destination, the speed drops off exponentially.
     * Instead of landing very slowly, a better experience is achieved if the final
     * destination is arrived at quicker.
     */
    private static final double LINEAR_DISTANCE_TAKE_OVER = 8.0;

    /**
     * The natural frequency of the stretch spring.
     */
@@ -587,6 +603,7 @@ public class EdgeEffect {
            if (mState == STATE_RECEDE) {
                updateSpring();
            }
            if (mDistance != 0f) {
                RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
                if (mTmpMatrix == null) {
                    mTmpMatrix = new Matrix();
@@ -637,6 +654,7 @@ public class EdgeEffect {
                        mHeight // max vertical stretch in pixels
                    );
                }
            }
        } else {
            // Animations have been disabled or this is TYPE_STRETCH and drawing into a Canvas
            // that isn't a Recording Canvas, so no effect can be shown. Just end the effect.
@@ -730,6 +748,26 @@ public class EdgeEffect {
        if (deltaT < 0.001f) {
            return; // Must have at least 1 ms difference
        }
        mStartTime = time;

        if (Math.abs(mVelocity) <= LINEAR_VELOCITY_TAKE_OVER
                && Math.abs(mDistance * mHeight) < LINEAR_DISTANCE_TAKE_OVER
                && Math.signum(mVelocity) == -Math.signum(mDistance)
        ) {
            // This is close. The spring will slowly reach the destination. Instead, we
            // will interpolate linearly so that it arrives at its destination quicker.
            mVelocity = Math.signum(mVelocity) * LINEAR_VELOCITY_TAKE_OVER;

            float targetDistance = mDistance + (mVelocity * deltaT / mHeight);
            if (Math.signum(targetDistance) != Math.signum(mDistance)) {
                // We have arrived
                mDistance = 0;
                mVelocity = 0;
            } else {
                mDistance = targetDistance;
            }
            return;
        }
        final double mDampedFreq = NATURAL_FREQUENCY * Math.sqrt(1 - DAMPING_RATIO * DAMPING_RATIO);

        // We're always underdamped, so we can use only those equations:
@@ -745,9 +783,7 @@ public class EdgeEffect {
                + mDampedFreq * sinCoeff * Math.cos(mDampedFreq * deltaT));
        mDistance = (float) distance / mHeight;
        mVelocity = (float) velocity;
        mStartTime = time;
        if (isAtEquilibrium()) {
            mState = STATE_IDLE;
            mDistance = 0;
            mVelocity = 0;
        }
+13 −3
Original line number Diff line number Diff line
@@ -40,9 +40,7 @@ public:

    StretchEffect() {}

    bool isEmpty() const {
        return MathUtils::isZero(mStretchDirection.x()) && MathUtils::isZero(mStretchDirection.y());
    }
    bool isEmpty() const { return isZero(mStretchDirection.x()) && isZero(mStretchDirection.y()); }

    void setEmpty() {
        *this = StretchEffect{};
@@ -114,6 +112,18 @@ public:
    }

private:
    // The epsilon for StretchEffect is less than in MathUtils because
    // the range is 0-1 for an entire screen and should be significantly
    // less than 1 pixel for a smooth stretch animation.
    inline static bool isZero(float value) {
        // Using fabsf is more performant as ARM computes
        // fabsf in a single instruction.
        return fabsf(value) <= NON_ZERO_EPSILON;
    }
    // This should be good for 1/25,000 of a screen and should be good for
    // screens with less than ~8000 pixels in one dimension with only 1/4 pixel
    // cut-off.
    static constexpr float NON_ZERO_EPSILON = 0.00004f;
    static sk_sp<SkRuntimeEffect> getStretchEffect();
    mutable SkVector mStretchDirection{0, 0};
    mutable std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;