Fixing animation player not overshooting spring animation; tuning springs
=> setCurrentPlayTime is bounded between [0, duration] by the animation framework Instead using interpolator so that we can go outside the bounds => Tune spring stiffness and dampening for overview card dismiss animations Bug: 154061408 Change-Id: Iaa31491fff499db916b36d9779ec159b8a89a2de
This commit is contained in:
parent
af560750d0
commit
7f9e8e2d27
|
@ -199,13 +199,12 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
|
|||
RecentsView.CONTENT_ALPHA, values);
|
||||
case INDEX_RECENTS_TRANSLATE_X_ANIM:
|
||||
// TODO: Do not assume motion across X axis for adjacent page
|
||||
return new SpringAnimationBuilder<>(
|
||||
mLauncher.getOverviewPanel(), ADJACENT_PAGE_OFFSET)
|
||||
return new SpringAnimationBuilder(mLauncher)
|
||||
.setMinimumVisibleChange(1f / mLauncher.getOverviewPanel().getWidth())
|
||||
.setDampingRatio(0.8f)
|
||||
.setStiffness(250)
|
||||
.setValues(values)
|
||||
.build(mLauncher);
|
||||
.build(mLauncher.getOverviewPanel(), ADJACENT_PAGE_OFFSET);
|
||||
case INDEX_PAUSE_TO_OVERVIEW_ANIM: {
|
||||
StateAnimationConfig config = new StateAnimationConfig();
|
||||
config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
|
||||
|
|
|
@ -28,6 +28,7 @@ import android.animation.Animator;
|
|||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
|
@ -185,13 +186,13 @@ public class StaggeredWorkspaceAnim {
|
|||
ResourceProvider rp = DynamicResource.provider(v.getContext());
|
||||
float stiffness = rp.getFloat(R.dimen.staggered_stiffness);
|
||||
float damping = rp.getFloat(R.dimen.staggered_damping_ratio);
|
||||
ObjectAnimator springTransY = new SpringAnimationBuilder<>(v, VIEW_TRANSLATE_Y)
|
||||
ValueAnimator springTransY = new SpringAnimationBuilder(v.getContext())
|
||||
.setStiffness(stiffness)
|
||||
.setDampingRatio(damping)
|
||||
.setMinimumVisibleChange(1f)
|
||||
.setEndValue(0)
|
||||
.setStartVelocity(mVelocity)
|
||||
.build(v.getContext());
|
||||
.build(v, VIEW_TRANSLATE_Y);
|
||||
springTransY.setStartDelay(startDelay);
|
||||
mAnimators.play(springTransY);
|
||||
|
||||
|
|
|
@ -125,11 +125,11 @@
|
|||
<item name="all_apps_spring_damping_ratio" type="dimen" format="float">0.75</item>
|
||||
<item name="all_apps_spring_stiffness" type="dimen" format="float">600</item>
|
||||
|
||||
<item name="dismiss_task_trans_y_damping_ratio" type="dimen" format="float">0.5</item>
|
||||
<item name="dismiss_task_trans_y_stiffness" type="dimen" format="float">1500</item>
|
||||
<item name="dismiss_task_trans_y_damping_ratio" type="dimen" format="float">0.73</item>
|
||||
<item name="dismiss_task_trans_y_stiffness" type="dimen" format="float">800</item>
|
||||
|
||||
<item name="dismiss_task_trans_x_damping_ratio" type="dimen" format="float">0.5</item>
|
||||
<item name="dismiss_task_trans_x_stiffness" type="dimen" format="float">1500</item>
|
||||
<item name="dismiss_task_trans_x_damping_ratio" type="dimen" format="float">0.73</item>
|
||||
<item name="dismiss_task_trans_x_stiffness" type="dimen" format="float">800</item>
|
||||
|
||||
<item name="horizontal_spring_damping_ratio" type="dimen" format="float">0.8</item>
|
||||
<item name="horizontal_spring_stiffness" type="dimen" format="float">400</item>
|
||||
|
|
|
@ -27,7 +27,6 @@ import android.animation.AnimatorSet;
|
|||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.util.FloatProperty;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
|
@ -64,19 +63,6 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL
|
|||
return new AnimatorPlaybackController(anim, duration, childAnims);
|
||||
}
|
||||
|
||||
private static final FloatProperty<ValueAnimator> CURRENT_PLAY_TIME =
|
||||
new FloatProperty<ValueAnimator>("current-play-time") {
|
||||
@Override
|
||||
public void setValue(ValueAnimator animator, float v) {
|
||||
animator.setCurrentPlayTime((long) v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(ValueAnimator animator) {
|
||||
return (float) animator.getCurrentPlayTime();
|
||||
}
|
||||
};
|
||||
|
||||
// Progress factor after which an animation is considered almost completed.
|
||||
private static final float ANIMATION_COMPLETE_THRESHOLD = 0.95f;
|
||||
|
||||
|
@ -177,21 +163,22 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL
|
|||
long springDuration = animationDuration;
|
||||
for (Holder h : mChildAnimations) {
|
||||
if ((h.springProperty.flags & springFlag) != 0) {
|
||||
SpringAnimationBuilder s = new SpringAnimationBuilder(h.anim, CURRENT_PLAY_TIME)
|
||||
.setStartValue(clampDuration(mCurrentFraction))
|
||||
.setEndValue(goingToEnd ? h.anim.getDuration() : 0)
|
||||
.setStartVelocity(scaledVelocity * h.anim.getDuration())
|
||||
SpringAnimationBuilder s = new SpringAnimationBuilder(context)
|
||||
.setStartValue(mCurrentFraction)
|
||||
.setEndValue(goingToEnd ? 1 : 0)
|
||||
.setStartVelocity(scaledVelocity)
|
||||
.setMinimumVisibleChange(scaleInverse)
|
||||
.setDampingRatio(h.springProperty.mDampingRatio)
|
||||
.setStiffness(h.springProperty.mStiffness);
|
||||
.setStiffness(h.springProperty.mStiffness)
|
||||
.computeParams();
|
||||
|
||||
long expectedDurationL = s.build(context).getDuration();
|
||||
long expectedDurationL = s.getDuration();
|
||||
springDuration = Math.max(expectedDurationL, springDuration);
|
||||
|
||||
float expectedDuration = expectedDurationL;
|
||||
h.setter = (a, l) ->
|
||||
s.setValue(a, mAnimationPlayer.getCurrentPlayTime() / expectedDuration);
|
||||
h.anim.setInterpolator(LINEAR);
|
||||
h.setter = (a, l) -> a.setCurrentFraction(
|
||||
mAnimationPlayer.getCurrentPlayTime() / expectedDuration);
|
||||
h.anim.setInterpolator(s::getInterpolatedValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
*/
|
||||
package com.android.launcher3.anim;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.util.FloatProperty;
|
||||
|
||||
|
@ -28,10 +30,9 @@ import com.android.launcher3.util.DefaultDisplay;
|
|||
* Utility class to build an object animator which follows the same path as a spring animation for
|
||||
* an underdamped spring.
|
||||
*/
|
||||
public class SpringAnimationBuilder<T> extends FloatProperty<T> {
|
||||
public class SpringAnimationBuilder {
|
||||
|
||||
private final T mTarget;
|
||||
private final FloatProperty<T> mProperty;
|
||||
private final Context mContext;
|
||||
|
||||
private float mStartValue;
|
||||
private float mEndValue;
|
||||
|
@ -64,27 +65,23 @@ public class SpringAnimationBuilder<T> extends FloatProperty<T> {
|
|||
private double mValueThreshold;
|
||||
private double mVelocityThreshold;
|
||||
|
||||
private float mCurrentTime = 0;
|
||||
private float mDuration = 0;
|
||||
|
||||
public SpringAnimationBuilder(T target, FloatProperty<T> property) {
|
||||
super("dynamic-spring-property");
|
||||
mTarget = target;
|
||||
mProperty = property;
|
||||
|
||||
mStartValue = mProperty.get(target);
|
||||
public SpringAnimationBuilder(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setEndValue(float value) {
|
||||
public SpringAnimationBuilder setEndValue(float value) {
|
||||
mEndValue = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setStartValue(float value) {
|
||||
public SpringAnimationBuilder setStartValue(float value) {
|
||||
mStartValue = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setValues(float... values) {
|
||||
public SpringAnimationBuilder setValues(float... values) {
|
||||
if (values.length > 1) {
|
||||
mStartValue = values[0];
|
||||
mEndValue = values[values.length - 1];
|
||||
|
@ -94,7 +91,7 @@ public class SpringAnimationBuilder<T> extends FloatProperty<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setStiffness(
|
||||
public SpringAnimationBuilder setStiffness(
|
||||
@FloatRange(from = 0.0, fromInclusive = false) float stiffness) {
|
||||
if (stiffness <= 0) {
|
||||
throw new IllegalArgumentException("Spring stiffness constant must be positive.");
|
||||
|
@ -103,7 +100,7 @@ public class SpringAnimationBuilder<T> extends FloatProperty<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setDampingRatio(
|
||||
public SpringAnimationBuilder setDampingRatio(
|
||||
@FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
|
||||
float dampingRatio) {
|
||||
if (dampingRatio <= 0 || dampingRatio >= 1) {
|
||||
|
@ -113,7 +110,7 @@ public class SpringAnimationBuilder<T> extends FloatProperty<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setMinimumVisibleChange(
|
||||
public SpringAnimationBuilder setMinimumVisibleChange(
|
||||
@FloatRange(from = 0.0, fromInclusive = false) float minimumVisibleChange) {
|
||||
if (minimumVisibleChange <= 0) {
|
||||
throw new IllegalArgumentException("Minimum visible change must be positive.");
|
||||
|
@ -122,25 +119,21 @@ public class SpringAnimationBuilder<T> extends FloatProperty<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setStartVelocity(float startVelocity) {
|
||||
public SpringAnimationBuilder setStartVelocity(float startVelocity) {
|
||||
mVelocity = startVelocity;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(T object, float time) {
|
||||
mCurrentTime = time;
|
||||
mProperty.setValue(
|
||||
object, (float) (exponentialComponent(time) * cosSinX(time)) + mEndValue);
|
||||
public float getInterpolatedValue(float fraction) {
|
||||
return getValue(mDuration * fraction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(T t) {
|
||||
return mCurrentTime;
|
||||
private float getValue(float time) {
|
||||
return (float) (exponentialComponent(time) * cosSinX(time)) + mEndValue;
|
||||
}
|
||||
|
||||
public ObjectAnimator build(Context context) {
|
||||
int singleFrameMs = DefaultDisplay.getSingleFrameMs(context);
|
||||
public SpringAnimationBuilder computeParams() {
|
||||
int singleFrameMs = DefaultDisplay.getSingleFrameMs(mContext);
|
||||
double naturalFreq = Math.sqrt(mStiffness);
|
||||
double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
|
||||
|
||||
|
@ -187,12 +180,21 @@ public class SpringAnimationBuilder<T> extends FloatProperty<T> {
|
|||
}
|
||||
} while (true);
|
||||
|
||||
mDuration = (float) duration;
|
||||
return this;
|
||||
}
|
||||
|
||||
long durationMs = (long) (1000.0 * duration);
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(mTarget, this, 0, (float) duration);
|
||||
animator.setDuration(durationMs).setInterpolator(Interpolators.LINEAR);
|
||||
animator.addListener(AnimationSuccessListener.forRunnable(
|
||||
() -> mProperty.setValue(mTarget, mEndValue)));
|
||||
public long getDuration() {
|
||||
return (long) (1000.0 * mDuration);
|
||||
}
|
||||
|
||||
public <T> ValueAnimator build(T target, FloatProperty<T> property) {
|
||||
computeParams();
|
||||
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(0, mDuration);
|
||||
animator.setDuration(getDuration()).setInterpolator(LINEAR);
|
||||
animator.addUpdateListener(anim ->
|
||||
property.set(target, getInterpolatedValue(anim.getAnimatedFraction())));
|
||||
return animator;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue