xref: /aosp_15_r20/external/lottie/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieClipSpec.kt (revision bb5273fecd5c61b9ace70f9ff4fcd88f0e12e3f7)
1 package com.airbnb.lottie.compose
2 
3 import com.airbnb.lottie.LottieComposition
4 
5 /**
6  * Use subclasses of [LottieClipSpec] to set min/max bounds on the animation playback.
7  *
8  * @see LottieAnimation
9  * @see rememberLottieAnimatable
10  * @see animateLottieCompositionAsState
11  */
12 sealed class LottieClipSpec {
13 
getMinProgressnull14     internal abstract fun getMinProgress(composition: LottieComposition): Float
15 
16     internal abstract fun getMaxProgress(composition: LottieComposition): Float
17 
18     /**
19      * Play the animation between these two frames. [maxInclusive] determines whether the animation
20      * should play the max frame or stop one frame before it.
21      */
22     data class Frame(
23         val min: Int? = null,
24         val max: Int? = null,
25         val maxInclusive: Boolean = true,
26     ) : LottieClipSpec() {
27 
28         private val actualMaxFrame = when {
29             max == null -> null
30             maxInclusive -> max
31             else -> max - 1
32         }
33 
34         override fun getMinProgress(composition: LottieComposition): Float {
35             return when (min) {
36                 null -> 0f
37                 else -> (min / composition.endFrame).coerceIn(0f, 1f)
38             }
39         }
40 
41         override fun getMaxProgress(composition: LottieComposition): Float {
42             return when (actualMaxFrame) {
43                 null -> 1f
44                 else -> (actualMaxFrame / composition.endFrame).coerceIn(0f, 1f)
45             }
46         }
47     }
48 
49     /**
50      * Play the animation between these two progress values.
51      */
52     data class Progress(
53         val min: Float = 0f,
54         val max: Float = 1f,
55     ) : LottieClipSpec() {
getMinProgressnull56         override fun getMinProgress(composition: LottieComposition): Float {
57             return min
58         }
59 
getMaxProgressnull60         override fun getMaxProgress(composition: LottieComposition): Float {
61             return max
62         }
63     }
64 
65     /**
66      * Play the animation from minMarker until maxMarker. If maxMarker represents the end of your animation,
67      * set [maxInclusive] to true. If the marker represents the beginning of the next section, set
68      * it to false to stop the animation at the frame before maxMarker.
69      */
70     data class Markers(
71         val min: String? = null,
72         val max: String? = null,
73         val maxInclusive: Boolean = true
74     ) : LottieClipSpec() {
getMinProgressnull75         override fun getMinProgress(composition: LottieComposition): Float {
76             return when (min) {
77                 null -> 0f
78                 else -> ((composition.getMarker(min)?.startFrame ?: 0f) / composition.endFrame).coerceIn(0f, 1f)
79             }
80         }
81 
getMaxProgressnull82         override fun getMaxProgress(composition: LottieComposition): Float {
83             return when (max) {
84                 null -> 1f
85                 else -> {
86                     val offset = if (maxInclusive) 0 else -1
87                     return ((composition.getMarker(max)?.startFrame?.plus(offset) ?: 0f) / composition.endFrame).coerceIn(0f, 1f)
88                 }
89             }
90         }
91     }
92 
93     /**
94      * Play the animation from the beginning of the marker for the duration of the marker itself.
95      * The duration can be set in After Effects.
96      */
97     data class Marker(val marker: String) : LottieClipSpec() {
getMinProgressnull98         override fun getMinProgress(composition: LottieComposition): Float {
99             return ((composition.getMarker(marker)?.startFrame ?: 0f) / composition.endFrame).coerceIn(0f, 1f)
100         }
101 
getMaxProgressnull102         override fun getMaxProgress(composition: LottieComposition): Float {
103             val marker = composition.getMarker(marker) ?: return 1f
104             return ((marker.startFrame + marker.durationFrames) / composition.endFrame).coerceIn(0f, 1f)
105         }
106     }
107 }