Compottie

repository·standalone-main·Indexed 20 days ago

https://github.com/alexzhirkevich/compottie

A Compose Multiplatform library for rendering Lottie animations across Android, iOS, JVM, macOS, and Web. It supports various animation sources via LottieCompositionSpec, including JSON strings, dotLottie files, CMP resources, and URLs. The library provides tools for animation progress control via animateLottieCompositionAsState and LottieAnimatable, as well as support for state machines, custom fonts, external assets, and runtime modification of animation properties through Dynamic Properties.

Tokens
6.8K
Snippets
25
Records
26
Agent score
61%

What's inside Compottie

  1. How to animate or update Lottie progress

    standalone-main

    Compottie provides two primary ways to control animation progress, designed to be analogous to Jetpack Compose APIs. Both implement LottieAnimationState (which is a State<Float>).

    Use animateLottieCompositionAsState() for simple animations

    Use this when your animation is simple or is a direct function of other state properties. It is analogous to animate*AsState.

    val progress by animateLottieCompositionAsState(composition)
    
    // With iterations
    val progress by animateLottieCompositionAsState(
        composition,
        iterations = Compottie.IterateForever,
    )
    
    // With clipping
    val progress by animateLottieCompositionAsState(
        composition,
        clipSpec = LottieClipSpec.Progress(0.5f, 0.75f),
    )

    Use LottieAnimatable for imperative control

    Use this when you need to manually trigger animations (e.g., calling animate or snapTo inside a LaunchedEffect). It is analogous to Animatable.

    val lottieAnimatable = rememberLottieAnimatable()
    
    LaunchedEffect(composition) {
        lottieAnimatable.animate(
            composition,
            iterations = Compottie.IterateForever,
            clipSpec = LottieClipSpec.Progress(0.5f, 0.75f),
        )
    }
    // animateLottieCompositionAsState example
    val progress by animateLottieCompositionAsState(composition)
    
    // LottieAnimatable example
    val lottieAnimatable = rememberLottieAnimatable()
    LaunchedEffect(Unit) {
        lottieAnimatable.animate(
            composition,
            iterations = Compottie.IterateForever,
            clipSpec = LottieClipSpec.Progress(0.5f, 0.75f),
        )
    }
  2. How LottieCompositionSpec works

    standalone-main

    LottieCompositionSpec is an open interface used to define the source of your animation. Different implementations allow you to load animations from various formats and locations:

    • LottieCompositionSpec.JsonString(string): Load from a JSON string.
    • LottieCompositionSpec.DotLottie(bytes): Load from dotLottie bytes.
    • LottieCompositionSpec.Resource(uri): Load from a CMP resource URI.
    • LottieCompositionSpec.Url(url): Load from a web URL.
    • LottieCompositionSpec.RawRes(resId): Load from Android raw resources.

    Example usage:

    val jsonAnim by rememberLottieComposition {
        LottieCompositionSpec.JsonString(Res.readBytes("files/anim.json").decodeToString())
    }
    
    val dotLottieAnim by rememberLottieComposition {
        LottieCompositionSpec.DotLottie(Res.readBytes("files/anim.lottie"))
    }
    
    val urlAnim by rememberLottieComposition(
        LottieCompositionSpec.Url("https://example.com/anim.lottie")
    )
    val jsonAnim by rememberLottieComposition {
        LottieCompositionSpec.JsonString(
            Res.readBytes("files/anim.json").decodeToString()
        )
    }
    
    val dotLottieAnim by rememberLottieComposition {
        LottieCompositionSpec.DotLottie(
            Res.readBytes("files/anim.lottie")
        )
    }
    
    // jsonAnim and dotLottieAnim can both be replaced with
    
    val resAnim by rememberLottieComposition(
        LottieCompositionSpec.Resource(Res.getUri("files/anim.json"))
    //    LottieCompositionSpec.Resource(Res.getUri("files/anim.lottie"))
    )
    
    val urlAnim by rememberLottieComposition(
        LottieCompositionSpec.Url("https://example.com/anim.lottie")
    )
  3. Understand Lottie Layer hierarchy and keypaths

    standalone-main

    Lottie animations are structured as a hierarchy of layers. Understanding this hierarchy is essential for targeting specific elements with dynamic properties.

    Layer Types

    • Shape Layer: A combination of vector shapes (e.g., ellipses, paths).
    • Image Layer: A raster image (embedded or external).
    • Precomposition Layer: A special layer that acts as a container for a group of other layers.

    Keypaths

    To target an element, you must follow its path from the root.

    • For a layer inside a precomposition: ["Precomposition Name", "Layer Name"].
    • For a shape inside a group within a layer: ["Layer Name", "Group Name", "Shape Name"].

    If you are unsure of the structure, you can inspect your Lottie JSON file using the LottieFiles JSON editor.

  4. Use wildcards in Lottie dynamic properties

    standalone-main

    When defining dynamic properties, you can use wildcards to target multiple layers or shapes without specifying exact paths:

    • **: Matches any level of depth (recursive wildcard).
    • *: Matches exactly one level deep.

    This is helpful when you want to apply a change to every layer with a specific name, regardless of where it sits in the hierarchy.

    val painter = rememberLottiePainter(
        composition = composition,
        dynamicProperties = rememberLottieDynamicProperties {
            // Targets every layer named 'Shape Layer 4' at any depth
            shapeLayer("**", "Shape Layer 4") {
                transform {
                    rotation { current -> current * progress }
                }
                // Targets every fill named 'Fill 4' exactly one level deep
                fill("*", "Fill 4") {
                    color { Color.Red }
                    alpha { .5f }
                }
            }
        }
    )
  5. Install Compottie via Gradle

    standalone-main

    Compottie is a Compose Multiplatform library for rendering Lottie animations. Choose the module that matches your animation source requirements:

    • compottie: Main module with rendering engine and JsonString spec.
    • compottie-lite: Same as compottie but without expressions support (smaller binary size).
    • compottie-dot: Supports dotLottie and ZIP animation specs.
    • compottie-network: Supports Url spec and asset/font managers (uses Ktor3 and Okio).
    • compottie-network-core: Base implementation for network module to allow custom HTTP clients.
    • compottie-resources: Supports Resource spec using CMP resources.

    Note: For Android projects with minSdk < 26, the dot and network modules require desugaring.

    [versions]
    compottie="<version>"
    
    [libraries]
    compottie = { module = "io.github.alexzhirkevich:compottie", version.ref = "compottie" }
    compottie-lite = { module = "io.github.alexzhirkevich:compottie-lite", version.ref = "compottie" }
    compottie-dot = { module = "io.github.alexzhirkevich:compottie-dot", version.ref = "compottie" }
    compottie-network = { module = "io.github.alexzhirkevich:compottie-network", version.ref = "compottie" }
    compottie-resources = { module = "io.github.alexzhirkevich:compottie-resources", version.ref = "compottie" }
  6. Use custom fonts with LottieFontManager

    standalone-main

    To use custom fonts in your animations, pass a LottieFontManager to rememberLottiePainter using the fontManager parameter. The compottie-resources module provides an implementation for loading fonts from Compose Resources.

    val painter = rememberLottiePainter(
        composition = composition,
        fontManager = rememberResourcesFontManager {
            fontSpec ->
            when (fontSpec.family) {
                "Comic Neue" -> Res.font.ComicNeue
                else -> null // default font will be used
            }
        }
    )
  7. Use Dynamic Properties to update animations at runtime

    standalone-main

    Dynamic properties allow you to modify animation properties (like color, size, position, or rotation) while the animation is running. This is useful for implementing themes (day/night mode), localizing text, or reacting to user gestures.

    To use them, create a dynamicProperties object using rememberLottieDynamicProperties and pass it to rememberLottiePainter.

    Common use cases include:

    • Changing colors for app themes.
    • Localizing animation text.
    • Controlling specific layer progress (e.g., for download progress).
    • Responding to gestures by changing size or position.
    val painter = rememberLottiePainter(
        composition = composition,
        dynamicProperties = rememberLottieDynamicProperties {
            shapeLayer("Precomposition 1", "Shape Layer 4") {
                transform {
                    rotation { current -> current * progress }
                }
                fill("Group 1", "Fill 4") {
                    color { Color.Red }
                    alpha { .5f }
                }
            }
        }
    )
  8. Basic Usage of Compottie

    standalone-main

    To render a Lottie animation, you need to load a LottieComposition using a LottieCompositionSpec and then provide the progress to a painter.

    Using Image composable

    Use rememberLottieComposition to load the animation and animateLottieCompositionAsState to manage the progress state.

    val composition by rememberLottieComposition {
        LottieCompositionSpec.JsonString(
            Res.readBytes("files/anim.json").decodeToString()
        )
    }
    val progress by animateLottieCompositionAsState(composition)
    
    Image(
        painter = rememberLottiePainter(
            composition = composition,
            progress = { progress },
        ),
        contentDescription = "Lottie animation"
    )

    Using Lottie composable

    The Lottie composable is a higher-level component that supports state machines. It requires the compottie-resources dependency if using LottieCompositionSpec.Resource.

    val composition by rememberLottieComposition(
        LottieCompositionSpec.Resource(Res.getUri("files/anim.json"))
    )
    
    Lottie(
        painter = rememberLottiePainter(
            composition = composition,
            iterations = Compottie.IterateForever
        ),
        contentDescription = "Lottie animation"
    )
  9. Update animation properties with Dynamic Properties

    standalone-main

    You can modify animation properties (colors, transforms, opacity, etc.) at runtime using rememberLottieDynamicProperties. This is useful for theming, localization, or responding to gestures.

    Wildcards

    You can use wildcards to target layers or groups:

    • **: Any level deep wildcard.
    • *: One level deep wildcard.

    Performance Note

    Property building blocks (like rotation, color, or alpha) are called on every animation frame. If these operations are computationally expensive or involve allocations, cache them if they do not depend on the current progress.

    val painter = rememberLottiePainter(
        composition = composition,
        dynamicProperties = rememberLottieDynamicProperties {
            shapeLayer("Precomposition 1", "Shape Layer 4") {
                transform {
                    rotation { current -> current * progress }
                }
                fill("Group 1", "Fill 4") {
                    color { Color.Red }
                    alpha { .5f }
                }
                group("Group 4") {
                    ellipse("Ellipse 1") {
                        // configure size, position of the ellipse named "Ellipse 1"
                    }
                    stroke("Ellipse 1 Stroke") {
                        // configure stroke named "Ellipse 1 Stroke" in the same group
                    }
                }
            }
        }
    )
  10. Load external images with LottieAssetsManager

    standalone-main

    While Lottie supports baked-in or zipped images, you can also provide external images using a LottieAssetsManager. The compottie-resources module provides a ready-to-use implementation for loading assets from Compose Resources.

    Pass the manager to rememberLottiePainter via the assetsManager parameter.

    val painter = rememberLottiePainter(
        composition = composition,
        assetsManager = rememberResourcesAssetsManager(
            directory = "files", // by default
            readBytes = Res::readBytes
        )
    )
  11. Implement dotLottie State Machines

    standalone-main

    dotLottie files can include interactive state machines. To display an animation that uses a state machine, use LottieAnimatable for progress control and provide a stateMachine via rememberLottieStateMachine to the Lottie composable.

    val animatable = rememberLottieAnimatable()
    
    Lottie(
        painter = rememberLottiePainter(
            composition = dotLottieComposition,
            progress = animatable::value
        ),
        stateMachine = rememberLottieStateMachine(
            id = "state_machine_id",
            composition = dotLottieComposition,
            animatable = animatable
        ),
        contentDescription = "Interactive animation"
    )
  12. Apply themes to dotLottie animations

    standalone-main

    dotLottie files support multiple animation styles (themes). To select a specific theme, pass the theme parameter to the rememberLottiePainter function.

    val painter = rememberLottiePainter(
        composition = dotLottieComposition,
        progress = progress,
        theme = "night"
    )