24

I'm using Lottie for Android to add some animations in an app. In this app the primary and accent color can be chosen via the settings. I'm using an animation with a transparent background. To make the animation fit the chosen colors I'd like to add a color overlay to the animation, this way I can have one animation file but I can set the color programmatically.

Does anyone have an idea how I can manipulate the animation by adding a color overlay?

TylerH
  • 20,799
  • 66
  • 75
  • 101
SolveSoul
  • 2,448
  • 4
  • 25
  • 34

7 Answers7

53

To apply a color filter you'll need three things as of now:

  1. KeyPath (name of content you wish to edit)
  2. LottieProperty (name of property you wish to edit)
  3. LottieValueCallback (callback called for every animation re-render)

The layer name can be found in the JSON of the animation by the tag 'nm'.

Add a full color overlay:

LottieAnimationView animationView = findViewById(R.id.animation_view);
animationView.addValueCallback(
        new KeyPath("**"),
        LottieProperty.COLOR_FILTER,
        new SimpleLottieValueCallback<ColorFilter>() {
            @Override
            public ColorFilter getValue(LottieFrameInfo<ColorFilter> frameInfo) {
                return new PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.SRC_ATOP);
            }
        }
);

Add a single layer color overlay (layer called "checkmark"):

LottieAnimationView animationView = findViewById(R.id.animation_view);
animationView.addValueCallback(
        new KeyPath("checkmark", "**"),
        LottieProperty.COLOR_FILTER,
        new SimpleLottieValueCallback<ColorFilter>() {
            @Override
            public ColorFilter getValue(LottieFrameInfo<ColorFilter> frameInfo) {
                return new PorterDuffColorFilter(Color.CYAN, PorterDuff.Mode.SRC_ATOP);
            }
        }
);

Remove any color overlays:

LottieAnimationView animationView = findViewById(R.id.animation_view);
animationView.addValueCallback(new KeyPath("**"), LottieProperty.COLOR_FILTER,
        new SimpleLottieValueCallback<ColorFilter>() {
            @Override
            public ColorFilter getValue(LottieFrameInfo<ColorFilter> frameInfo) {
                return null;
            }
        }
);

You can read all about it in the official documentation.

You can also check out this sample repository

Here's a visual on the results of the code snippets:

Example

SolveSoul
  • 2,448
  • 4
  • 25
  • 34
  • 13
    use app:lottie_colorFilter="@color/colorAccent" in case you want to access via xml – Shubham AgaRwal Aug 28 '17 at 00:04
  • 1
    addColorFilter not defined in Lottie. check plz – Rasoul Miri Jul 01 '18 at 07:58
  • 1
    Is it possible to overlay a gradient instead of a solid color? – Allan Jiang Mar 19 '19 at 18:42
  • 1
    Link of " the official documentation." is dead. Here's an archived version of it: https://web.archive.org/web/20180731092340/http://airbnb.io:80/lottie/android/dynamic.html . Maybe there is a newer link? – android developer May 13 '19 at 07:57
  • @androiddeveloper thanks for the heads-up, I changed the URL to the newer version of the documentation. – SolveSoul May 13 '19 at 09:57
  • @SolveSoul Say, can you please share a Github sample of how to color one part of the animation file? So far I've seen only changing color of the entire animation. – android developer May 13 '19 at 16:11
  • 1
    @androiddeveloper I went out of my way and created a sample repository that shows the Lottie 3.0.0 implementation. I've also updated the answer. Here you are: https://github.com/SolveSoul/lottie-android-colorfilter-sample – SolveSoul May 14 '19 at 08:35
  • @SolveSoul Thank you! Say, is there any difference between setting the color this way, and setting it using colorFilter? Performance hit? – android developer May 14 '19 at 21:23
  • Is there any way to animate the change in color? In the example the animation starts off empty, but what if I wanted to change from the original checkmark to one of the others while it was animating? – Myk Aug 07 '19 at 23:03
  • @Myk it's certainly possible but Lottie is being a bit flaky with reporting animation progress in the callback. Anyway, I added some sample code on how you could approach this: https://github.com/SolveSoul/lottie-android-colorfilter-sample/commit/192d522e4602c982958a39a7957296ee56387081#diff-b8c45f348186ee76426b68dc458b3473R109 – SolveSoul Aug 09 '19 at 08:25
15

Found in sources of lottie, based on main answer (thanks @SolveSoul ).

Java

First, get your color, for example:

int yourColor = ContextCompat.getColor(getContext(),R.color.colorPrimary);

Then set color filter like this:

SimpleColorFilter filter = new SimpleColorFilter(yourColor);
KeyPath keyPath = new KeyPath("**");
LottieValueCallback<ColorFilter> callback = new LottieValueCallback<ColorFilter>(filter);
animationView.addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback);

Kotlin

First, get your color, for example:

val yourColor = ContextCompat.getColor(context, R.color.colorPrimary)

Then set color filter like this:

val filter = SimpleColorFilter(yourColor)
val keyPath = KeyPath("**")
val callback: LottieValueCallback<ColorFilter> = LottieValueCallback(filter)

animationView.addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback)

Kotlin extension

fun LottieAnimationView.changeLayersColor(
    @ColorRes colorRes: Int
) {
    val color = ContextCompat.getColor(context, colorRes)
    val filter = SimpleColorFilter(color)
    val keyPath = KeyPath("**")
    val callback: LottieValueCallback<ColorFilter> = LottieValueCallback(filter)

    addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback)
}

Boken
  • 4,825
  • 10
  • 32
  • 42
Evgen
  • 520
  • 4
  • 17
4

Since you're passing a JSONObject containing all of the drawing data to Lottie when setting the animation, you could just replace some of the color values with your desired ones before you set it.

If you look for the color key c you'll probably find something like

...,"c":{"k":[1,0.7,0,1]},"fillEnabled":true,...

where changing those float values in that JSONArray would change the colors in the animation.

Granted, I'm not saying it will be too trivial to find/replace the correct values, but this should at least point you in that direction.

As a side note: once you find it, you could set the value in your asset to some kind of nice placeholder like "k":[ BG_COLOR_REPLACEMENT_1 ] and then when loading the asset, just run .replace("BG_COLOR_REPLACEMENT_1", "1,0.7,1,1"); on your String before creating the JSONObject and passing it to Lottie.

Cruceo
  • 6,763
  • 2
  • 31
  • 52
4

Compose + Lottie

@Composable
fun LottieAnimation(isPlaying: Boolean = false) {
    val composition by rememberLottieComposition(LottieCompositionSpec.Asset(LOTTIE_FILE_NAME))
    val progress by animateLottieCompositionAsState(
        composition,
        iterations = LottieConstants.IterateForever,
        isPlaying = isPlaying,
    )
    val dynamicProperties = rememberLottieDynamicProperties(
        rememberLottieDynamicProperty(
            property = LottieProperty.COLOR_FILTER,
            value = SimpleColorFilter(MaterialTheme.colors.primary.toArgb()),
            keyPath = arrayOf("**")
        ),
    )
    LottieAnimation(
        composition = composition,
        progress = { progress },
        dynamicProperties = dynamicProperties,
    )
}
Zoran
  • 1,502
  • 19
  • 22
2

I saw Guardanis answer and elaborated a safe way to find the colour inside the JSON that contains the Lottie animation:

Search for - "c":{"a" - and you will find fragments like this for each layer of you image: {"ty":"fl","c":{"a":0,"k":[0.4,0.4,0.4,0.4]}

In the fragment you will notice "c" means COLOUR, "a" means ALPHA and "k" is the CMYK of the colour of the layer. Just change it for the one that you want.

Roney Sampaio
  • 320
  • 3
  • 7
  • Just be wary that a JSONObject is an unordered mapping concept, and that those `a` and `k` keys may not appear lexicographically in your search. Spaces and newlines may also be the result of pretty-printing, so you may want to normalize it before searching and using regular expressions. – Cruceo Sep 28 '18 at 14:09
  • actually seems to be HSV colour code for me, not sure what the 4th value does though, playing in a preview doesn't seem to change anything – hmac Jan 24 '20 at 12:13
2

In Kotlin(v1.4.32) To set up full animation in all layers just do this :

YOURS_LottieAnimationView.addValueCallback(
            KeyPath("**"),
            LottieProperty.COLOR_FILTER,
            { PorterDuffColorFilter(Color.parseColor("#b70101"), PorterDuff.Mode.SRC_ATOP) }
        )
RIO DROID
  • 51
  • 3
-3

If your JSON has has an sc: field then you should be able to set the Hex color directly

like:

"sc": "#6664e7"
ahbou
  • 4,710
  • 23
  • 36