5 quick animations to make your Compose app stand out
浏览 5 个快速动画,让您的应用在短短几分钟内生动起来。
1. AnimatedVisibility()
下面使用简单的 if 语句来显示或隐藏文本。

为了提升 UI 效果,将不断变化的可见状态包装在 AnimatedVisibility
组合中,使得文本的显示或隐藏带有动画效果。

kotlin
@Composable
public fun AnimatedVisibility(
visible: Boolean,
modifier: Modifier = Modifier,
enter: EnterTransition = fadeIn() + expandIn(),
exit: ExitTransition = shrinkOut() + fadeOut(),
label: String = "AnimatedVisibility",
content: @Composable() AnimatedVisibilityScope.() -> Unit,
) {
val transition = updateTransition(visible, label)
AnimatedVisibilityImpl(transition, { it }, modifier, enter, exit, content = content)
}
AnimatedVisibility
还有一系列不同的自定义选项。
2. Modifier.animateContentSize()
通过更改 Text 在屏幕上绘制的最大行数,单击文本时切换展开状态,

我们可以使用 Modifier 轻松修复这种跳跃状态。只需要将 animateContentSize()
添加到 Modifier 链中,即可获得即时平滑的变化。

kotlin
public fun Modifier.animateContentSize(
animationSpec: FiniteAnimationSpec<IntSize> =
spring(
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = IntSize.VisibilityThreshold,
),
finishedListener: ((initialValue: IntSize, targetValue: IntSize) -> Unit)? = null,
): Modifier =
this.clipToBounds() then
SizeAnimationModifierElement(animationSpec, Alignment.TopStart, finishedListener)
如果想让动画变得更有弹性,可以更改 animateContentSize()
修饰符以接受动画规范。 大多数动画 API 都提供设置动画规范的功能,可以使用它来定义动画如何发生。
下面给动画添加弹簧规范,将刚度 stiffness 设置为低,并添加少量的弹跳 dampingRatio 为 DampingRatioLowBouncy。我们可以看到动画拥有了弹跳的效果。

3. AnimatedContent()
通过选择多 Tab 中的选项,来切换不同的组合内容。此时的内容切换十分生硬,内容之间没有任何的平滑过渡。

将切换内容代码包装在 AnimatedContent
组合中,提供将要转换到的目标状态。现在不同组合项之间的切换具有淡入淡出和相互缩放的效果,比之前好多了。

kotlin
@Composable
public fun <S> AnimatedContent(
targetState: S,
modifier: Modifier = Modifier,
transitionSpec: AnimatedContentTransitionScope<S>.() -> ContentTransform = {
(fadeIn(animationSpec = tween(220, delayMillis = 90)) +
scaleIn(initialScale = 0.92f, animationSpec = tween(220, delayMillis = 90)))
.togetherWith(fadeOut(animationSpec = tween(90)))
},
contentAlignment: Alignment = Alignment.TopStart,
label: String = "AnimatedContent",
contentKey: (targetState: S) -> Any? = { it },
content: @Composable() AnimatedContentScope.(targetState: S) -> Unit,
) {
val transition = updateTransition(targetState = targetState, label = label)
transition.AnimatedContent(
modifier,
transitionSpec,
contentAlignment,
contentKey,
content = content,
)
}
通过更改 AnimatedContent
的过渡规范,我们可以自定义屏幕上出现的的新可组合项如何进入屏幕以及旧可组合项如何退出屏幕。
使用 slideIntoContainer 将新内容向上滑动进入,使用 slideOutOfContainer 将旧内容向下滑动退出。我们还通过 animationSpec
设置动画的持续时间和缓动效果。

4. animateFloatAsState()
进度条计算进度并更新展示,这样的变化十分生硬,缺少平滑过渡。

使用 animateFloatAsState()
动态计算进度,使得进度条的变化产生动画效果。

kotlin
@Composable
public fun animateFloatAsState(
targetValue: Float,
animationSpec: AnimationSpec<Float> = defaultAnimation,
visibilityThreshold: Float = 0.01f,
label: String = "FloatAnimation",
finishedListener: ((Float) -> Unit)? = null,
): State<Float> {
val resolvedAnimSpec =
if (animationSpec === defaultAnimation) {
remember(visibilityThreshold) { spring(visibilityThreshold = visibilityThreshold) }
} else {
animationSpec
}
return animateValueAsState(
targetValue,
Float.VectorConverter,
resolvedAnimSpec,
visibilityThreshold,
label,
finishedListener,
)
}
5. rememberInfiniteTransition() with Custom DrawScope drawing
实现自定义彩虹边框,颜色围绕着图像轮廓旋转。这里需要使用无限执行的动画。

- 使用
rememberInfiniteTransition()
创建一个无限动画InfiniteTransition
声明为infiniteTransition
。
kotlin
@Composable
public fun rememberInfiniteTransition(label: String = "InfiniteTransition"): InfiniteTransition {
val infiniteTransition = remember { InfiniteTransition(label) }
infiniteTransition.run()
return infiniteTransition
}
- 调用
infiniteTransition.animateFloat()
创建一个动态变化的 State 声明为rotationAnimation
。
kotlin
@Composable
public fun InfiniteTransition.animateFloat(
initialValue: Float,
targetValue: Float,
animationSpec: InfiniteRepeatableSpec<Float>,
label: String = "FloatAnimation",
): State<Float> =
animateValue(initialValue, targetValue, Float.VectorConverter, animationSpec, label)
- 在 Image 的
Modifier.drawBehind{}
代码块中调用rotate()
方法,参数degrees
角度传入上一步声明的rotationAnimation
。
kotlin
/** Draw into a [Canvas] behind the modified content. */
fun Modifier.drawBehind(onDraw: DrawScope.() -> Unit) = this then DrawBehindElement(onDraw)
inline fun DrawScope.rotate(degrees: Float, pivot: Offset = center, block: DrawScope.() -> Unit) =
withTransform({ rotate(degrees, pivot) }, block)
- 如此在绘制彩虹边框背景的时候,边框会不停地围绕图像旋转。

infiniteRepeatable()
创建一个无限重复动画规格
kotlin
@Stable
public fun <T> infiniteRepeatable(
animation: DurationBasedAnimationSpec<T>,
repeatMode: RepeatMode = RepeatMode.Restart,
initialStartOffset: StartOffset = StartOffset(0),
): InfiniteRepeatableSpec<T> = InfiniteRepeatableSpec(animation, repeatMode, initialStartOffset)
