Skip to content

Commit

Permalink
Allow shape contents to have dynamic properties for its path (#2439)
Browse files Browse the repository at this point in the history
Fixes #2389
  • Loading branch information
gpeal authored Dec 29, 2023
1 parent 9f8951a commit 6e11040
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.airbnb.lottie.compose

import android.graphics.Bitmap
import android.graphics.ColorFilter
import android.graphics.Path
import android.graphics.PointF
import android.graphics.Typeface
import androidx.compose.runtime.Composable
Expand Down Expand Up @@ -102,6 +103,7 @@ class LottieDynamicProperties internal constructor(
private val typefaceProperties: List<LottieDynamicProperty<Typeface>>,
private val bitmapProperties: List<LottieDynamicProperty<Bitmap>>,
private val charSequenceProperties: List<LottieDynamicProperty<CharSequence>>,
private val pathProperties: List<LottieDynamicProperty<Path>>,
) {
@Suppress("UNCHECKED_CAST")
constructor(properties: List<LottieDynamicProperty<*>>) : this(
Expand All @@ -114,6 +116,7 @@ class LottieDynamicProperties internal constructor(
properties.filter { it.property is Typeface } as List<LottieDynamicProperty<Typeface>>,
properties.filter { it.property is Bitmap } as List<LottieDynamicProperty<Bitmap>>,
properties.filter { it.property is CharSequence } as List<LottieDynamicProperty<CharSequence>>,
properties.filter { it.property is Path } as List<LottieDynamicProperty<Path>>,
)

internal fun addTo(drawable: LottieDrawable) {
Expand Down Expand Up @@ -144,6 +147,9 @@ class LottieDynamicProperties internal constructor(
charSequenceProperties.forEach { p ->
drawable.addValueCallback(p.keyPath, p.property, p.callback.toValueCallback())
}
pathProperties.forEach { p ->
drawable.addValueCallback(p.keyPath, p.property, p.callback.toValueCallback())
}
}

internal fun removeFrom(drawable: LottieDrawable) {
Expand Down Expand Up @@ -174,11 +180,14 @@ class LottieDynamicProperties internal constructor(
charSequenceProperties.forEach { p ->
drawable.addValueCallback(p.keyPath, p.property, null as LottieValueCallback<CharSequence>?)
}
pathProperties.forEach { p ->
drawable.addValueCallback(p.keyPath, p.property, null as LottieValueCallback<Path>?)
}
}
}

private fun <T> ((frameInfo: LottieFrameInfo<T>) -> T).toValueCallback() = object : LottieValueCallback<T>() {
override fun getValue(frameInfo: LottieFrameInfo<T>): T {
return invoke(frameInfo)
}
}
}
12 changes: 12 additions & 0 deletions lottie/src/main/java/com/airbnb/lottie/LottieProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.graphics.Bitmap;
import android.graphics.ColorFilter;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Typeface;

Expand Down Expand Up @@ -220,4 +221,15 @@ public interface LottieProperty {
* Replace the text for a text layer.
*/
CharSequence TEXT = "dynamic_text";

/**
* Replace a path. This can only be used on path contents. For other shapes such as rectangles and polystars,
* use LottieProperties corresponding to their specific properties.
* <p>
* If you need to do any operations on the path such as morphing, use the Jetpack androidx.graphics.path library.
* <p>
* In After Effects, any of those other shapes can be converted to a bezier path by right clicking it and
* selecting "Convert To Bezier Path".
*/
Path PATH = new Path();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@
import androidx.annotation.Nullable;

import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.animation.keyframe.BaseKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.ShapeKeyframeAnimation;
import com.airbnb.lottie.model.KeyPath;
import com.airbnb.lottie.model.content.ShapePath;
import com.airbnb.lottie.model.content.ShapeTrimPath;
import com.airbnb.lottie.model.layer.BaseLayer;
import com.airbnb.lottie.utils.MiscUtils;
import com.airbnb.lottie.value.LottieValueCallback;

import java.util.ArrayList;
import java.util.List;

public class ShapeContent implements PathContent, BaseKeyframeAnimation.AnimationListener {
public class ShapeContent implements PathContent, BaseKeyframeAnimation.AnimationListener, KeyPathElementContent {
private final Path path = new Path();

private final String name;
Expand Down Expand Up @@ -65,7 +69,7 @@ private void invalidate() {
}

@Override public Path getPath() {
if (isPathValid) {
if (isPathValid && !shapeAnimation.hasValueCallback()) {
return path;
}

Expand Down Expand Up @@ -94,4 +98,17 @@ private void invalidate() {
@Override public String getName() {
return name;
}

@Override public void resolveKeyPath(
KeyPath keyPath, int depth, List<KeyPath> accumulator, KeyPath currentPartialKeyPath) {
MiscUtils.resolveKeyPath(keyPath, depth, accumulator, currentPartialKeyPath, this);
}

@SuppressWarnings("unchecked")
@Override
public <T> void addValueCallback(T property, @Nullable LottieValueCallback<T> callback) {
if (property == LottieProperty.PATH) {
shapeAnimation.setValueCallback((LottieValueCallback<Path>) callback);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ public void setValueCallback(@Nullable LottieValueCallback<A> valueCallback) {
}
}

public boolean hasValueCallback() {
return valueCallback != null;
}

/**
* keyframeProgress will be [0, 1] unless the interpolator has overshoot in which case, this
* should be able to handle values outside of that range.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
public class ShapeKeyframeAnimation extends BaseKeyframeAnimation<ShapeData, Path> {
private final ShapeData tempShapeData = new ShapeData();
private final Path tempPath = new Path();
private Path valueCallbackStartPath;
private Path valueCallbackEndPath;

private List<ShapeModifierContent> shapeModifiers;

Expand All @@ -33,6 +35,20 @@ public ShapeKeyframeAnimation(List<Keyframe<ShapeData>> keyframes) {
}
}
MiscUtils.getPathFromData(modifiedShapeData, tempPath);
if (valueCallback != null) {
if (valueCallbackStartPath == null) {
valueCallbackStartPath = new Path();
valueCallbackEndPath = new Path();
}
MiscUtils.getPathFromData(startShapeData, valueCallbackStartPath);
if (endShapeData != null) {
MiscUtils.getPathFromData(endShapeData, valueCallbackEndPath);
}

return valueCallback.getValueInternal(keyframe.startFrame, keyframe.endFrame,
valueCallbackStartPath, endShapeData == null ? valueCallbackStartPath : valueCallbackEndPath,
keyframeProgress, getLinearCurrentKeyframeProgress(), getProgress());
}
return tempPath;
}

Expand Down
Loading

0 comments on commit 6e11040

Please sign in to comment.