Merge "Linearly complete overscroll stretch when close" into sc-dev

This commit is contained in:
George Mount
2021-06-28 17:10:31 +00:00
committed by Android (Google) Code Review
2 changed files with 87 additions and 41 deletions

View File

@@ -97,12 +97,28 @@ public class EdgeEffect {
*/ */
private static final double VELOCITY_THRESHOLD = 0.01; 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 value threshold before the spring animation is considered close enough to
* the destination to be settled. This should be around 0.01 pixel. * the destination to be settled. This should be around 0.01 pixel.
*/ */
private static final double VALUE_THRESHOLD = 0.001; 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. * The natural frequency of the stretch spring.
*/ */
@@ -587,55 +603,57 @@ public class EdgeEffect {
if (mState == STATE_RECEDE) { if (mState == STATE_RECEDE) {
updateSpring(); updateSpring();
} }
RecordingCanvas recordingCanvas = (RecordingCanvas) canvas; if (mDistance != 0f) {
if (mTmpMatrix == null) { RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
mTmpMatrix = new Matrix(); if (mTmpMatrix == null) {
mTmpPoints = new float[12]; mTmpMatrix = new Matrix();
} mTmpPoints = new float[12];
//noinspection deprecation }
recordingCanvas.getMatrix(mTmpMatrix); //noinspection deprecation
recordingCanvas.getMatrix(mTmpMatrix);
mTmpPoints[0] = 0; mTmpPoints[0] = 0;
mTmpPoints[1] = 0; // top-left mTmpPoints[1] = 0; // top-left
mTmpPoints[2] = mWidth; mTmpPoints[2] = mWidth;
mTmpPoints[3] = 0; // top-right mTmpPoints[3] = 0; // top-right
mTmpPoints[4] = mWidth; mTmpPoints[4] = mWidth;
mTmpPoints[5] = mHeight; // bottom-right mTmpPoints[5] = mHeight; // bottom-right
mTmpPoints[6] = 0; mTmpPoints[6] = 0;
mTmpPoints[7] = mHeight; // bottom-left mTmpPoints[7] = mHeight; // bottom-left
mTmpPoints[8] = mWidth * mDisplacement; mTmpPoints[8] = mWidth * mDisplacement;
mTmpPoints[9] = 0; // drag start point mTmpPoints[9] = 0; // drag start point
mTmpPoints[10] = mWidth * mDisplacement; mTmpPoints[10] = mWidth * mDisplacement;
mTmpPoints[11] = mHeight * mDistance; // drag point mTmpPoints[11] = mHeight * mDistance; // drag point
mTmpMatrix.mapPoints(mTmpPoints); mTmpMatrix.mapPoints(mTmpPoints);
RenderNode renderNode = recordingCanvas.mNode; RenderNode renderNode = recordingCanvas.mNode;
float left = renderNode.getLeft() float left = renderNode.getLeft()
+ min(mTmpPoints[0], mTmpPoints[2], mTmpPoints[4], mTmpPoints[6]); + min(mTmpPoints[0], mTmpPoints[2], mTmpPoints[4], mTmpPoints[6]);
float top = renderNode.getTop() float top = renderNode.getTop()
+ min(mTmpPoints[1], mTmpPoints[3], mTmpPoints[5], mTmpPoints[7]); + min(mTmpPoints[1], mTmpPoints[3], mTmpPoints[5], mTmpPoints[7]);
float right = renderNode.getLeft() float right = renderNode.getLeft()
+ max(mTmpPoints[0], mTmpPoints[2], mTmpPoints[4], mTmpPoints[6]); + max(mTmpPoints[0], mTmpPoints[2], mTmpPoints[4], mTmpPoints[6]);
float bottom = renderNode.getTop() float bottom = renderNode.getTop()
+ max(mTmpPoints[1], mTmpPoints[3], mTmpPoints[5], mTmpPoints[7]); + max(mTmpPoints[1], mTmpPoints[3], mTmpPoints[5], mTmpPoints[7]);
// assume rotations of increments of 90 degrees // assume rotations of increments of 90 degrees
float x = mTmpPoints[10] - mTmpPoints[8]; float x = mTmpPoints[10] - mTmpPoints[8];
float width = right - left; float width = right - left;
float vecX = dampStretchVector(Math.max(-1f, Math.min(1f, x / width))); float vecX = dampStretchVector(Math.max(-1f, Math.min(1f, x / width)));
float y = mTmpPoints[11] - mTmpPoints[9]; float y = mTmpPoints[11] - mTmpPoints[9];
float height = bottom - top; float height = bottom - top;
float vecY = dampStretchVector(Math.max(-1f, Math.min(1f, y / height))); float vecY = dampStretchVector(Math.max(-1f, Math.min(1f, y / height)));
boolean hasValidVectors = Float.isFinite(vecX) && Float.isFinite(vecY); boolean hasValidVectors = Float.isFinite(vecX) && Float.isFinite(vecY);
if (right > left && bottom > top && mWidth > 0 && mHeight > 0 && hasValidVectors) { if (right > left && bottom > top && mWidth > 0 && mHeight > 0 && hasValidVectors) {
renderNode.stretch( renderNode.stretch(
vecX, // horizontal stretch intensity vecX, // horizontal stretch intensity
vecY, // vertical stretch intensity vecY, // vertical stretch intensity
mWidth, // max horizontal stretch in pixels mWidth, // max horizontal stretch in pixels
mHeight // max vertical stretch in pixels mHeight // max vertical stretch in pixels
); );
}
} }
} else { } else {
// Animations have been disabled or this is TYPE_STRETCH and drawing into a Canvas // Animations have been disabled or this is TYPE_STRETCH and drawing into a Canvas
@@ -730,6 +748,26 @@ public class EdgeEffect {
if (deltaT < 0.001f) { if (deltaT < 0.001f) {
return; // Must have at least 1 ms difference 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); final double mDampedFreq = NATURAL_FREQUENCY * Math.sqrt(1 - DAMPING_RATIO * DAMPING_RATIO);
// We're always underdamped, so we can use only those equations: // We're always underdamped, so we can use only those equations:
@@ -745,9 +783,7 @@ public class EdgeEffect {
+ mDampedFreq * sinCoeff * Math.cos(mDampedFreq * deltaT)); + mDampedFreq * sinCoeff * Math.cos(mDampedFreq * deltaT));
mDistance = (float) distance / mHeight; mDistance = (float) distance / mHeight;
mVelocity = (float) velocity; mVelocity = (float) velocity;
mStartTime = time;
if (isAtEquilibrium()) { if (isAtEquilibrium()) {
mState = STATE_IDLE;
mDistance = 0; mDistance = 0;
mVelocity = 0; mVelocity = 0;
} }

View File

@@ -40,9 +40,7 @@ public:
StretchEffect() {} StretchEffect() {}
bool isEmpty() const { bool isEmpty() const { return isZero(mStretchDirection.x()) && isZero(mStretchDirection.y()); }
return MathUtils::isZero(mStretchDirection.x()) && MathUtils::isZero(mStretchDirection.y());
}
void setEmpty() { void setEmpty() {
*this = StretchEffect{}; *this = StretchEffect{};
@@ -118,6 +116,18 @@ public:
} }
private: 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(); static sk_sp<SkRuntimeEffect> getStretchEffect();
mutable SkVector mStretchDirection{0, 0}; mutable SkVector mStretchDirection{0, 0};
mutable std::unique_ptr<SkRuntimeShaderBuilder> mBuilder; mutable std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;