compose-animations
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCompose: animations
Compose:动画
Official reference: Quick guide to Animations in Compose. See also Choose an animation API, Value-based animations, Animation modifiers and composables.
Core principle
核心原则
Pick the smallest API that matches the problem: built-in visibility and layout transitions first, then a single animated value, then a shared transition object when several values must move together, then gesture-level or imperative APIs when the framework cannot express the motion.
选择最贴合需求的最小化API:优先使用内置的可见性和布局过渡,其次是单个动画值,当多个值需要同步变化时使用共享过渡对象,当框架无法表达所需动效时再使用手势级或命令式API。
Pick the smallest animation API
选择最小化动画API
| Need | API |
|---|---|
| Show or hide a subtree with enter/exit semantics; content is removed after exit completes | |
| Animate one property toward a target derived from state | |
| Several animated values keyed off one boolean, enum, or sealed state | |
| Smooth size when child layout height/width changes (e.g. text wraps) | |
| Swap between different composable trees for the same slot | |
| User-driven motion (drag, fling, interruptible springs) | |
| 需求 | API |
|---|---|
| 为子树添加进入/退出语义的显示/隐藏动画;退出完成后移除内容 | |
| 基于状态驱动单个属性向目标值动画 | |
| 多个动画值由单个布尔值、枚举或密封状态驱动 | |
| 子布局高度/宽度变化时实现平滑尺寸过渡(如文本换行) | |
| 同一位置切换不同可组合树 | |
| 用户驱动的动效(拖拽、快速滑动、可中断弹簧动画) | |
Appear and disappear
显示与隐藏
Prefer when the UI should leave or join the tree with enter/exit transitions.
AnimatedVisibilitykotlin
AnimatedVisibility(visible = expanded) {
Text("Details…")
}animateFloatAsStateAnimatedVisibilityAnimatedVisibilityAnimatedContent当UI需要通过进入/退出过渡加入或离开组件树时,优先使用。
AnimatedVisibilitykotlin
AnimatedVisibility(visible = expanded) {
Text("Details…")
}仅对透明度使用只会实现淡入淡出效果;除非自行控制,否则该可组合项会保留在组合中并继续参与布局。当你有意保持子组件挂载(保留状态、焦点)但视觉上隐藏时,可以使用这种方式。若需要真正从组件树中移除的效果,请使用(或参考快速指南中的/条件组合模式)。
animateFloatAsStateAnimatedVisibilityAnimatedVisibilityAnimatedContentBackground color
背景颜色
Use for smooth color targets.
animateColorAsStateFor animated fills behind children, the quick guide recommends drawing with rather than so the animated color is applied in the draw phase appropriately for performance.
Modifier.drawBehindModifier.background()kotlin
val background = animateColorAsState(
targetValue = if (selected) selectedColor else idleColor,
label = "background",
)
Box(
Modifier.drawBehind { drawRect(background.value) },
) { /* content */ }使用实现平滑的颜色目标值过渡。
animateColorAsStatekotlin
val background = animateColorAsState(
targetValue = if (selected) selectedColor else idleColor,
label = "background",
)
Box(
Modifier.drawBehind { drawRect(background.value) },
) { /* content */ }Size changes
尺寸变化
Modifier.animateContentSize()Modifier.animateContentSize()Value-based animations (animate*AsState
)
animate*AsState基于值的动画(animate*AsState
)
animate*AsStateCompose provides for , , , , , , , , , and more. You supply the target; the API owns the animation state.
animate*AsStateFloatDpColorSizeOffsetRectIntIntOffsetIntSize- Pass an via
AnimationSpec(e.g.animationSpec,spring) when defaults are wrong for the UI.tween - Set a distinct for debugging and tooling when multiple animations exist in one composable.
label - For completion or sequencing details, see Value-based animations.
kotlin
val width by animateDpAsState(
targetValue = if (expanded) 200.dp else 56.dp,
animationSpec = spring(dampingRatio = 0.7f, stiffness = Spring.StiffnessMedium),
label = "fabWidth",
)Compose为、、、、、、、、等类型提供了。你只需提供目标值,API会管理动画状态。
FloatDpColorSizeOffsetRectIntIntOffsetIntSizeanimate*AsState- 当默认设置不符合UI需求时,可通过参数传入
animationSpec(如AnimationSpec、spring)。tween - 当一个可组合项中存在多个动画时,设置独特的****便于调试和工具分析。
label - 关于动画完成或序列编排的细节,请参考基于值的动画。
kotlin
val width by animateDpAsState(
targetValue = if (expanded) 200.dp else 56.dp,
animationSpec = spring(dampingRatio = 0.7f, stiffness = Spring.StiffnessMedium),
label = "fabWidth",
)Multiple properties: rememberTransition
rememberTransition多属性动画:rememberTransition
rememberTransitionWhen one piece of state (e.g. ) should drive several animated values in lockstep, use and define child animations on that transition:
enum class Phase { A, B, C }rememberTransitionkotlin
val transition = rememberTransition(targetState = phase, label = "phase")
val alpha by transition.animateFloat(label = "alpha") { target ->
if (target == Phase.Visible) 1f else 0f
}
val offset by transition.animateDp(label = "offset") { target ->
if (target == Phase.Visible) 0.dp else 24.dp
}Avoid multiple independent calls that should stay visually synchronized but can drift if specs or targets diverge. Older code may use ; prefer for new code.
animate*AsStateupdateTransitionrememberTransition当单个状态(如)需要同步驱动多个动画值时,使用并在该过渡上定义子动画:
enum class Phase { A, B, C }rememberTransitionkotlin
val transition = rememberTransition(targetState = phase, label = "phase")
val alpha by transition.animateFloat(label = "alpha") { target ->
if (target == Phase.Visible) 1f else 0f
}
val offset by transition.animateDp(label = "offset") { target ->
if (target == Phase.Visible) 0.dp else 24.dp
}避免使用多个独立的调用,即使它们应该保持视觉同步,但如果动画规格或目标值不一致,可能会出现偏差。旧代码可能使用;新代码请优先使用。
animate*AsStateupdateTransitionrememberTransitionChoosing between content-level APIs
内容级API选择
Use the official Choose an animation API tree when unsure. Compressed rules:
| Situation | Prefer |
|---|---|
| Same composable, different target values for layout properties | |
| Different composable content for the same region (tabs, steps) | |
| Pager-like swipe between pages | Horizontal pager APIs from the animation docs / Material—follow the choose-api guidance |
| Transitions owned by Navigation Compose | Use navigation’s built-in transitions rather than bolting |
Art-based motion (illustrations, Lottie, complex vector timelines) is outside this skill; use dedicated libraries and the “additional resources” links on Animations.
不确定时,请参考官方的选择合适的动画API决策树。简化规则如下:
| 场景 | 优先选择 |
|---|---|
| 同一可组合项,布局属性的目标值不同 | |
| 同一区域切换不同可组合内容(标签页、步骤页) | |
| 类似分页器的页面滑动切换 | 动画文档/ Material库中的水平分页器API——遵循选择API的指引 |
| 由Navigation Compose管理的过渡 | 使用导航内置的过渡,不要在相同的目的地切换上额外叠加 |
艺术化动效(插画、Lottie、复杂矢量时间线)不属于本技能范畴;请使用专用库,并参考动画页面的“额外资源”链接。
Decision flow (high level)
决策流程(概览)
mermaid
flowchart TD
start[Animation_need]
start --> showHide{Show_or_hide_subtree}
showHide -->|yes| av[AnimatedVisibility]
showHide -->|no| oneProp{Single_property_to_target}
oneProp -->|yes| asState["animate*AsState"]
oneProp -->|no| multiProp{Many_props_one_state}
multiProp -->|yes| rt[rememberTransition]
multiProp -->|no| swapTree{Different_composable_content}
swapTree -->|yes| ac[AnimatedContent_or_Crossfade]
swapTree -->|no| advanced[Animatable_or_lower_level]mermaid
flowchart TD
start[动画需求]
start --> showHide{是否显示/隐藏子树}
showHide -->|是| av[AnimatedVisibility]
showHide -->|否| oneProp{是否为单个属性目标动画}
oneProp -->|是| asState["animate*AsState"]
oneProp -->|否| multiProp{是否为单状态驱动多属性}
multiProp -->|是| rt[rememberTransition]
multiProp -->|否| swapTree{是否切换不同可组合内容}
swapTree -->|是| ac[AnimatedContent或Crossfade]
swapTree -->|否| advanced[Animatable或更低层级API]AnimatedContent keys for state holders
AnimatedContent的状态持有者键
When receives a state-holder wrapper such as , , or a sealed , decide what should actually trigger the transition. Usually the animation should run when the content shape changes (loading → content → error), not when the payload inside the same shape changes.
AnimatedContentAsyncResult<T>Result<T>UiStateUse to map rich state to the animation identity:
contentKeykotlin
AnimatedContent(
targetState = result,
contentKey = { state ->
when (state) {
AsyncResult.Loading -> "loading"
is AsyncResult.Success -> "content"
is AsyncResult.Error -> "error"
}
},
label = "profile-content",
) { state ->
when (state) {
AsyncResult.Loading -> Loading()
is AsyncResult.Success -> Profile(state.value)
is AsyncResult.Error -> ErrorMessage(state.throwable)
}
}Without , every unequal can be treated as new content. That is useful if a payload change should animate, but noisy when fresh data updates the same screen shape.
contentKeySuccess(value)Choose keys by visual shape:
| State change | Typical |
|---|---|
| Loading → Success → Error | Branch key: |
| Success item A → Success item B should crossfade | Stable item id |
| Success data refresh should update in place | Constant content key for |
| Error message text changes but error UI shape stays | Constant content key for |
当接收状态持有者包装类(如、或密封类)时,需要确定什么情况应该触发过渡。通常,当内容形态变化(加载中→内容→错误)时才应该运行动画,而不是同一形态下的 payload 更新时。
AnimatedContentAsyncResult<T>Result<T>UiState使用将复杂状态映射为动画标识:
contentKeykotlin
AnimatedContent(
targetState = result,
contentKey = { state ->
when (state) {
AsyncResult.Loading -> "loading"
is AsyncResult.Success -> "content"
is AsyncResult.Error -> "error"
}
},
label = "profile-content",
) { state ->
when (state) {
AsyncResult.Loading -> Loading()
is AsyncResult.Success -> Profile(state.value)
is AsyncResult.Error -> ErrorMessage(state.throwable)
}
}如果不设置,每个不同的都会被视为新内容。如果payload变化需要触发动画,这种方式是有用的,但当新数据更新同一屏幕形态时,会产生不必要的动画。
contentKeySuccess(value)根据视觉形态选择键:
| 状态变化 | 典型 |
|---|---|
| 加载中→成功→错误 | 分支键: |
| 成功项A→成功项B需要淡入淡出 | 稳定的项ID |
| 成功数据刷新需要原地更新 | 为 |
| 错误消息文本变化但错误UI形态不变 | 为 |
Animated values and composition performance
动画值与组合性能
animate*AsStateStateModifier.offsetModifier.graphicsLayerbycompose-state-deferred-readsIf recomposition counters spike during motion unrelated to bad stability, see .
compose-recomposition-performanceanimate*AsStateStateModifier.offsetModifier.graphicsLayerbycompose-state-deferred-reads如果在动效期间重组计数器激增且与稳定性无关,请参考。
compose-recomposition-performanceAdvanced pointers (read the linked docs)
进阶指引(阅读链接文档)
- Gesture-driven or cancelable motion: with
Animatable, decay, andsnapTo—Advanced animation example: Gestures and Drag, swipe, and fling.pointerInput - Infinite or repeating cycles: .
rememberInfiniteTransition - Seekable / test-controlled progress: and related APIs for predictable timelines in tests or tooling.
SeekableTransitionState
- 手势驱动或可取消动效:结合的
Animatable、衰减函数和snapTo——高级动画示例:手势和拖拽、滑动与快速滑动。pointerInput - 无限或重复循环动画:。
rememberInfiniteTransition - 可搜索/测试控制进度:及相关API,用于测试或工具中的可预测时间线。
SeekableTransitionState
Common mistakes
常见错误
| Mistake | Fix |
|---|---|
Fade with | Use |
Three | One |
Animated color on | Prefer |
Chaining | Prefer |
| Ignoring Navigation’s own transitions | Use Nav APIs for destination transitions; do not duplicate with |
| Add |
| 错误 | 修复方案 |
|---|---|
使用 | 使用 |
使用三个 | 使用一个 |
在 | 根据快速指南,优先使用 |
为简单的目标动画链式使用 | 优先使用 |
| 忽略Navigation自身的过渡 | 使用导航API处理目的地过渡;不要在相同的切换上重复使用 |
| 根据视觉形态或稳定项标识添加 |
When not to use this skill
不适用本技能的场景
- Side-effect timing (, clicks launching work): use
LaunchedEffect.compose-side-effects - Deep performance tuning of where snapshot state is read: use as the primary reference.
compose-state-deferred-reads
- 副作用时机(、点击触发任务):使用
LaunchedEffect。compose-side-effects - 快照状态读取的深度性能调优:以为主要参考文档。
compose-state-deferred-reads