blob: 18b46b7d1d43fd4996a275ce6c67c85ed6bd06d5 [file] [log] [blame]
package com.airbnb.lottie.utils;
import android.animation.ValueAnimator;
import android.support.annotation.FloatRange;
/**
* This is a slightly modified {@link ValueAnimator} that allows us to update start and end values
* easily optimizing for the fact that we know that it's a value animator with 2 floats.
*/
public class LottieValueAnimator extends ValueAnimator {
private boolean systemAnimationsAreDisabled = false;
private float compositionDuration;
private float speed = 1f;
@FloatRange(from = 0f, to = 1f) private float value = 0f;
@FloatRange(from = 0f, to = 1f) private float minValue = 0f;
@FloatRange(from = 0f, to = 1f) private float maxValue = 1f;
public LottieValueAnimator() {
setInterpolator(null);
addUpdateListener(new AnimatorUpdateListener() {
@Override public void onAnimationUpdate(ValueAnimator animation) {
// On older devices, getAnimatedValue and getAnimatedFraction
// will always return 0 if animations are disabled.
if (!systemAnimationsAreDisabled) {
value = (float) animation.getAnimatedValue();
}
}
});
updateValues();
}
public void systemAnimationsAreDisabled() {
this.systemAnimationsAreDisabled = true;
}
public void setCompositionDuration(float compositionDuration) {
this.compositionDuration = compositionDuration;
updateValues();
}
/**
* Sets the current animator value. This will update the play time as well.
* It will also be clamped to the values set with {@link #setMinValue(float)} and
* {@link #setMaxValue(float)}.
*/
public void setValue(@FloatRange(from = 0f, to = 1f) float value) {
value = MiscUtils.clamp(value, minValue, maxValue);
this.value = value;
float distFromStart = isReversed() ? (maxValue - value) : (value - minValue);
float range = Math.abs(maxValue - minValue);
float animatedPercentage = distFromStart / range;
if (getDuration() > 0) {
setCurrentPlayTime(Math.round(getDuration() * animatedPercentage));
}
}
public float getValue() {
return value;
}
public void setMinAndMaxValues(
@FloatRange(from = 0f, to = 1f) float minValue,
@FloatRange(from = 0f, to = 1f) float maxValue) {
this.minValue = minValue;
this.maxValue = maxValue;
updateValues();
}
public void setMinValue(@FloatRange(from = 0f, to = 1f) float minValue) {
if (minValue >= maxValue) {
throw new IllegalArgumentException("Min value must be smaller then max value.");
}
this.minValue = minValue;
updateValues();
}
public float getMinValue() {
return minValue;
}
public void setMaxValue(@FloatRange(from = 0f, to = 1f) float maxValue) {
if (maxValue <= minValue) {
throw new IllegalArgumentException("Max value must be greater than min value.");
}
this.maxValue = maxValue;
updateValues();
}
public void reverseAnimationSpeed() {
setSpeed(-getSpeed());
}
public void setSpeed(float speed) {
this.speed = speed;
updateValues();
}
public float getSpeed() {
return speed;
}
public void playAnimation() {
start();
setValue(isReversed() ? maxValue : minValue);
}
public void endAnimation() {
end();
}
public void pauseAnimation() {
float value = this.value;
cancel();
setValue(value);
}
public void resumeAnimation() {
float value = this.value;
if (isReversed() && this.value == minValue) {
value = maxValue;
} else if (!isReversed() && this.value == maxValue) {
value = minValue;
}
start();
setValue(value);
}
private boolean isReversed() {
return speed < 0;
}
/**
* Update the float values of the animator, scales the duration for the current min/max range
* and updates the play time so that it matches the new min/max range.
*/
private void updateValues() {
setDuration((long) (compositionDuration * (maxValue - minValue) / Math.abs(speed)));
setFloatValues(
speed < 0 ? maxValue : minValue,
speed < 0 ? minValue : maxValue
);
// This will force the play time to be correct for the current value.
setValue(value);
}
}