blob: 3fc0ef26837f0626f75a52f03e032e634995ab83 [file] [log] [blame]
package com.airbnb.lottie.animation.keyframe;
import android.graphics.Matrix;
import android.graphics.PointF;
import androidx.annotation.Nullable;
import com.airbnb.lottie.value.LottieValueCallback;
import com.airbnb.lottie.value.ScaleXY;
import com.airbnb.lottie.model.animatable.AnimatableTransform;
import com.airbnb.lottie.model.layer.BaseLayer;
import static com.airbnb.lottie.LottieProperty.TRANSFORM_ANCHOR_POINT;
import static com.airbnb.lottie.LottieProperty.TRANSFORM_END_OPACITY;
import static com.airbnb.lottie.LottieProperty.TRANSFORM_OPACITY;
import static com.airbnb.lottie.LottieProperty.TRANSFORM_POSITION;
import static com.airbnb.lottie.LottieProperty.TRANSFORM_ROTATION;
import static com.airbnb.lottie.LottieProperty.TRANSFORM_SCALE;
import static com.airbnb.lottie.LottieProperty.TRANSFORM_START_OPACITY;
public class TransformKeyframeAnimation {
private final Matrix matrix = new Matrix();
private final BaseKeyframeAnimation<PointF, PointF> anchorPoint;
private final BaseKeyframeAnimation<?, PointF> position;
private final BaseKeyframeAnimation<ScaleXY, ScaleXY> scale;
private final BaseKeyframeAnimation<Float, Float> rotation;
private final BaseKeyframeAnimation<Integer, Integer> opacity;
// Used for repeaters
@Nullable private final BaseKeyframeAnimation<?, Float> startOpacity;
@Nullable private final BaseKeyframeAnimation<?, Float> endOpacity;
public TransformKeyframeAnimation(AnimatableTransform animatableTransform) {
anchorPoint = animatableTransform.getAnchorPoint().createAnimation();
position = animatableTransform.getPosition().createAnimation();
scale = animatableTransform.getScale().createAnimation();
rotation = animatableTransform.getRotation().createAnimation();
opacity = animatableTransform.getOpacity().createAnimation();
if (animatableTransform.getStartOpacity() != null) {
startOpacity = animatableTransform.getStartOpacity().createAnimation();
} else {
startOpacity = null;
}
if (animatableTransform.getEndOpacity() != null) {
endOpacity = animatableTransform.getEndOpacity().createAnimation();
} else {
endOpacity = null;
}
}
public void addAnimationsToLayer(BaseLayer layer) {
layer.addAnimation(anchorPoint);
layer.addAnimation(position);
layer.addAnimation(scale);
layer.addAnimation(rotation);
layer.addAnimation(opacity);
if (startOpacity != null) {
layer.addAnimation(startOpacity);
}
if (endOpacity != null) {
layer.addAnimation(endOpacity);
}
}
public void addListener(final BaseKeyframeAnimation.AnimationListener listener) {
anchorPoint.addUpdateListener(listener);
position.addUpdateListener(listener);
scale.addUpdateListener(listener);
rotation.addUpdateListener(listener);
opacity.addUpdateListener(listener);
if (startOpacity != null) {
startOpacity.addUpdateListener(listener);
}
if (endOpacity != null) {
endOpacity.addUpdateListener(listener);
}
}
public void setProgress(float progress) {
anchorPoint.setProgress(progress);
position.setProgress(progress);
scale.setProgress(progress);
rotation.setProgress(progress);
opacity.setProgress(progress);
if (startOpacity != null) {
startOpacity.setProgress(progress);
}
if (endOpacity != null) {
endOpacity.setProgress(progress);
}
}
public BaseKeyframeAnimation<?, Integer> getOpacity() {
return opacity;
}
@Nullable public BaseKeyframeAnimation<?, Float> getStartOpacity() {
return startOpacity;
}
@Nullable public BaseKeyframeAnimation<?, Float> getEndOpacity() {
return endOpacity;
}
public Matrix getMatrix() {
matrix.reset();
PointF position = this.position.getValue();
if (position.x != 0 || position.y != 0) {
matrix.preTranslate(position.x, position.y);
}
float rotation = this.rotation.getValue();
if (rotation != 0f) {
matrix.preRotate(rotation);
}
ScaleXY scaleTransform = this.scale.getValue();
if (scaleTransform.getScaleX() != 1f || scaleTransform.getScaleY() != 1f) {
matrix.preScale(scaleTransform.getScaleX(), scaleTransform.getScaleY());
}
PointF anchorPoint = this.anchorPoint.getValue();
if (anchorPoint.x != 0 || anchorPoint.y != 0) {
matrix.preTranslate(-anchorPoint.x, -anchorPoint.y);
}
return matrix;
}
/**
* TODO: see if we can use this for the main {@link #getMatrix()} method.
*/
public Matrix getMatrixForRepeater(float amount) {
PointF position = this.position.getValue();
PointF anchorPoint = this.anchorPoint.getValue();
ScaleXY scale = this.scale.getValue();
float rotation = this.rotation.getValue();
matrix.reset();
matrix.preTranslate(position.x * amount, position.y * amount);
matrix.preScale(
(float) Math.pow(scale.getScaleX(), amount),
(float) Math.pow(scale.getScaleY(), amount));
matrix.preRotate(rotation * amount, anchorPoint.x, anchorPoint.y);
return matrix;
}
/**
* Returns whether the callback was applied.
*/
@SuppressWarnings("unchecked")
public <T> boolean applyValueCallback(T property, @Nullable LottieValueCallback<T> callback) {
if (property == TRANSFORM_ANCHOR_POINT) {
anchorPoint.setValueCallback((LottieValueCallback<PointF>) callback);
} else if (property == TRANSFORM_POSITION) {
position.setValueCallback((LottieValueCallback<PointF>) callback);
} else if (property == TRANSFORM_SCALE) {
scale.setValueCallback((LottieValueCallback<ScaleXY>) callback);
} else if (property == TRANSFORM_ROTATION) {
rotation.setValueCallback((LottieValueCallback<Float>) callback);
} else if (property == TRANSFORM_OPACITY) {
opacity.setValueCallback((LottieValueCallback<Integer>) callback);
} else if (property == TRANSFORM_START_OPACITY && startOpacity != null) {
startOpacity.setValueCallback((LottieValueCallback<Float>) callback);
} else if (property == TRANSFORM_END_OPACITY && endOpacity != null) {
endOpacity.setValueCallback((LottieValueCallback<Float>) callback);
} else {
return false;
}
return true;
}
}