<lambda>null1 package com.airbnb.lottie.compose
2
3 import android.graphics.Matrix
4 import android.graphics.Typeface
5 import androidx.annotation.FloatRange
6 import androidx.compose.foundation.Canvas
7 import androidx.compose.foundation.layout.Box
8 import androidx.compose.runtime.Composable
9 import androidx.compose.runtime.getValue
10 import androidx.compose.runtime.mutableStateOf
11 import androidx.compose.runtime.remember
12 import androidx.compose.runtime.setValue
13 import androidx.compose.ui.Alignment
14 import androidx.compose.ui.Modifier
15 import androidx.compose.ui.geometry.Size
16 import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
17 import androidx.compose.ui.graphics.nativeCanvas
18 import androidx.compose.ui.layout.ContentScale
19 import androidx.compose.ui.layout.ScaleFactor
20 import androidx.compose.ui.unit.IntSize
21 import com.airbnb.lottie.AsyncUpdates
22 import com.airbnb.lottie.LottieComposition
23 import com.airbnb.lottie.LottieDrawable
24 import com.airbnb.lottie.RenderMode
25 import kotlin.math.roundToInt
26
27 /**
28 * This is the base LottieAnimation composable. It takes a composition and renders it at a specific progress.
29 *
30 * The overloaded version of [LottieAnimation] that handles playback and is sufficient for most use cases.
31 *
32 * @param composition The composition that will be rendered. To generate a [LottieComposition], you can use
33 * [rememberLottieComposition].
34 * @param progress A provider for the progress (between 0 and 1) that should be rendered. If you want to render a
35 * specific frame, you can use [LottieComposition.getFrameForProgress]. In most cases, you will want
36 * to use one of the overloaded LottieAnimation composables that drives the animation for you.
37 * The overloads that have isPlaying as a parameter instead of progress will drive the
38 * animation automatically. You may want to use this version if you want to drive the animation
39 * from your own Animatable or via events such as download progress or a gesture.
40 * @param outlineMasksAndMattes Enable this to debug slow animations by outlining masks and mattes.
41 * The performance overhead of the masks and mattes will be proportional to the
42 * surface area of all of the masks/mattes combined.
43 * DO NOT leave this enabled in production.
44 * @param applyOpacityToLayers Sets whether to apply opacity to the each layer instead of shape.
45 * Opacity is normally applied directly to a shape. In cases where translucent
46 * shapes overlap, applying opacity to a layer will be more accurate at the
47 * expense of performance.
48 * Note: This process is very expensive. The performance impact will be reduced
49 * when hardware acceleration is enabled.
50 * @param enableMergePaths Enables experimental merge paths support. Most animations with merge paths will
51 * want this on but merge path support is more limited than some other rendering
52 * features so it defaults to off. The only way to know if your animation will work
53 * well with merge paths or not is to try it. If your animation has merge paths and
54 * doesn't render correctly, please file an issue.
55 * @param renderMode Allows you to specify whether you want Lottie to use hardware or software rendering.
56 * Defaults to AUTOMATIC. Refer to [LottieAnimationView.setRenderMode] for more info.
57 * @param maintainOriginalImageBounds When true, dynamically set bitmaps will be drawn with the exact bounds of the original animation,
58 * regardless of the bitmap size.
59 * When false, dynamically set bitmaps will be drawn at the top left of the original image but with its own bounds.
60 * Defaults to false.
61 * @param dynamicProperties Allows you to change the properties of an animation dynamically. To use them, use
62 * [rememberLottieDynamicProperties]. Refer to its docs for more info.
63 * @param alignment Define where the animation should be placed within this composable if it has a different
64 * size than this composable.
65 * @param contentScale Define how the animation should be scaled if it has a different size than this Composable.
66 * @param clipToCompositionBounds Determines whether or not Lottie will clip the animation to the original animation composition bounds.
67 * The composition bounds refers to the Lottie animation composition, not the Compose composition.
68 * @param clipTextToBoundingBox When true, if there is a bounding box set on a text layer (paragraph text), any text
69 * that overflows past its height will not be drawn.
70 * @param fontMap A map of keys to Typefaces. The key can be: "fName", "fFamily", or "fFamily-fStyle" as specified in your Lottie file.
71 * @param asyncUpdates When set to true, some parts of animation updates will be done off of the main thread.
72 * For more details, refer to the docs of [AsyncUpdates].
73 * @param safeMode If set to true, draw will be wrapped with a try/catch which will cause Lottie to
74 * render an empty frame rather than crash your app.
75 */
76 @Composable
77 @JvmOverloads
78 fun LottieAnimation(
79 composition: LottieComposition?,
80 progress: () -> Float,
81 modifier: Modifier = Modifier,
82 outlineMasksAndMattes: Boolean = false,
83 applyOpacityToLayers: Boolean = false,
84 enableMergePaths: Boolean = false,
85 renderMode: RenderMode = RenderMode.AUTOMATIC,
86 maintainOriginalImageBounds: Boolean = false,
87 dynamicProperties: LottieDynamicProperties? = null,
88 alignment: Alignment = Alignment.Center,
89 contentScale: ContentScale = ContentScale.Fit,
90 clipToCompositionBounds: Boolean = true,
91 clipTextToBoundingBox: Boolean = false,
92 fontMap: Map<String, Typeface>? = null,
93 asyncUpdates: AsyncUpdates = AsyncUpdates.AUTOMATIC,
94 safeMode: Boolean = false,
95 ) {
96 val drawable = remember { LottieDrawable() }
97 val matrix = remember { Matrix() }
98 var setDynamicProperties: LottieDynamicProperties? by remember(composition) { mutableStateOf(null) }
99
100 if (composition == null || composition.duration == 0f) return Box(modifier)
101
102 val bounds = composition.bounds
103 Canvas(
104 modifier = modifier
105 .lottieSize(bounds.width(), bounds.height())
106 ) {
107 drawIntoCanvas { canvas ->
108 val compositionSize = Size(bounds.width().toFloat(), bounds.height().toFloat())
109 val intSize = IntSize(size.width.roundToInt(), size.height.roundToInt())
110
111 val scale = contentScale.computeScaleFactor(compositionSize, size)
112 val translation = alignment.align(compositionSize * scale, intSize, layoutDirection)
113 matrix.reset()
114 matrix.preTranslate(translation.x.toFloat(), translation.y.toFloat())
115 matrix.preScale(scale.scaleX, scale.scaleY)
116
117 drawable.enableMergePathsForKitKatAndAbove(enableMergePaths)
118 drawable.setSafeMode(safeMode)
119 drawable.renderMode = renderMode
120 drawable.asyncUpdates = asyncUpdates
121 drawable.composition = composition
122 drawable.setFontMap(fontMap)
123 if (dynamicProperties !== setDynamicProperties) {
124 setDynamicProperties?.removeFrom(drawable)
125 dynamicProperties?.addTo(drawable)
126 setDynamicProperties = dynamicProperties
127 }
128 drawable.setOutlineMasksAndMattes(outlineMasksAndMattes)
129 drawable.isApplyingOpacityToLayersEnabled = applyOpacityToLayers
130 drawable.maintainOriginalImageBounds = maintainOriginalImageBounds
131 drawable.clipToCompositionBounds = clipToCompositionBounds
132 drawable.clipTextToBoundingBox = clipTextToBoundingBox
133 drawable.progress = progress()
134 drawable.setBounds(0, 0, bounds.width(), bounds.height())
135 drawable.draw(canvas.nativeCanvas, matrix)
136 }
137 }
138 }
139
140 /**
141 * This is like [LottieAnimation] except that it takes a raw progress parameter instead of taking a progress provider.
142 *
143 * @see LottieAnimation
144 */
145 @Composable
146 @Deprecated("Pass progress as a lambda instead of a float. This overload will be removed in the next release.")
LottieAnimationnull147 fun LottieAnimation(
148 composition: LottieComposition?,
149 @FloatRange(from = 0.0, to = 1.0) progress: Float,
150 modifier: Modifier = Modifier,
151 outlineMasksAndMattes: Boolean = false,
152 applyOpacityToLayers: Boolean = false,
153 enableMergePaths: Boolean = false,
154 renderMode: RenderMode = RenderMode.AUTOMATIC,
155 maintainOriginalImageBounds: Boolean = false,
156 dynamicProperties: LottieDynamicProperties? = null,
157 alignment: Alignment = Alignment.Center,
158 contentScale: ContentScale = ContentScale.Fit,
159 clipToCompositionBounds: Boolean = true,
160 safeMode: Boolean = false,
161 asyncUpdates: AsyncUpdates = AsyncUpdates.AUTOMATIC,
162 ) {
163 LottieAnimation(
164 composition = composition,
165 progress = { progress },
166 modifier = modifier,
167 outlineMasksAndMattes = outlineMasksAndMattes,
168 applyOpacityToLayers = applyOpacityToLayers,
169 enableMergePaths = enableMergePaths,
170 renderMode = renderMode,
171 maintainOriginalImageBounds = maintainOriginalImageBounds,
172 dynamicProperties = dynamicProperties,
173 alignment = alignment,
174 contentScale = contentScale,
175 clipToCompositionBounds = clipToCompositionBounds,
176 asyncUpdates = asyncUpdates,
177 safeMode = safeMode
178 )
179 }
180
181 /**
182 * This is like [LottieAnimation] except that it handles driving the animation via [animateLottieCompositionAsState]
183 * instead of taking a progress provider.
184 *
185 * @see LottieAnimation
186 * @see animateLottieCompositionAsState
187 */
188 @Composable
189 @JvmOverloads
LottieAnimationnull190 fun LottieAnimation(
191 composition: LottieComposition?,
192 modifier: Modifier = Modifier,
193 isPlaying: Boolean = true,
194 restartOnPlay: Boolean = true,
195 clipSpec: LottieClipSpec? = null,
196 speed: Float = 1f,
197 iterations: Int = 1,
198 outlineMasksAndMattes: Boolean = false,
199 applyOpacityToLayers: Boolean = false,
200 enableMergePaths: Boolean = false,
201 renderMode: RenderMode = RenderMode.AUTOMATIC,
202 reverseOnRepeat: Boolean = false,
203 maintainOriginalImageBounds: Boolean = false,
204 dynamicProperties: LottieDynamicProperties? = null,
205 alignment: Alignment = Alignment.Center,
206 contentScale: ContentScale = ContentScale.Fit,
207 clipToCompositionBounds: Boolean = true,
208 clipTextToBoundingBox: Boolean = false,
209 fontMap: Map<String, Typeface>? = null,
210 safeMode: Boolean = false,
211 asyncUpdates: AsyncUpdates = AsyncUpdates.AUTOMATIC,
212 ) {
213 val progress by animateLottieCompositionAsState(
214 composition,
215 isPlaying,
216 restartOnPlay,
217 reverseOnRepeat,
218 clipSpec,
219 speed,
220 iterations,
221 )
222 LottieAnimation(
223 composition = composition,
224 progress = { progress },
225 modifier = modifier,
226 outlineMasksAndMattes = outlineMasksAndMattes,
227 applyOpacityToLayers = applyOpacityToLayers,
228 enableMergePaths = enableMergePaths,
229 renderMode = renderMode,
230 maintainOriginalImageBounds = maintainOriginalImageBounds,
231 dynamicProperties = dynamicProperties,
232 alignment = alignment,
233 contentScale = contentScale,
234 clipToCompositionBounds = clipToCompositionBounds,
235 clipTextToBoundingBox = clipTextToBoundingBox,
236 fontMap = fontMap,
237 asyncUpdates = asyncUpdates,
238 safeMode = safeMode
239 )
240 }
241
timesnull242 private operator fun Size.times(scale: ScaleFactor): IntSize {
243 return IntSize((width * scale.scaleX).toInt(), (height * scale.scaleY).toInt())
244 }
245