Merge "Linearly complete overscroll stretch when close" into sc-dev
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user