mcpField
C
compose-animations

compose-animations

github.com/chrisbanes设计与 UI1.1k1.9k 次安装1 天前更新
代码仓库

关于 compose-animations

面向 Jetpack Compose 开发者的动画 API 选择与评审技能,依据动画目标和生命周期推荐合适 API,并强调内容身份与性能处理。

主要功能

  1. 01按动画需求选择 Compose API
  2. 02区分状态动画与手势动画
  3. 03检查动画生命周期与内容身份
  4. 04提供性能和测试建议

适用场景

  1. 01为显示或隐藏界面选择动画
  2. 02协调多个状态驱动的动画值
  3. 03审查 AnimatedContent 的内容切换
  4. 04排查逐帧动画的布局或绘制性能

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/chrisbanes/skills --skill compose-animations

安装后在 Claude Code 中输入/compose-animations确认已加载。

附带文件references/animated-content.md

详细介绍

Compose:动画

核心原则

选择能够表达动效及其生命周期的最简 API。

流程

  1. 明确任务:显示或隐藏子树、动画化单个值、协调由同一状态驱动的多个值、调整内容大小、切换内容,或处理用户驱动的动效。
  2. 从表格中选择相应的 API。优先使用目标状态 API;只有在需要手势、处理中断或命令式控制时,才使用 Animatable。
  3. 检查生命周期:alpha 动画会让内容继续参与组合;AnimatedVisibility 则会在退出动画结束后移除内容。需要卸载内容时,不要使用淡出效果。
  4. 对于 AnimatedContent,从内容 lambda 的目标值渲染内容;只有当视觉身份与载荷相等性不一致时,才选择 contentKey。在不同项目之间切换时,将目标值保留为身份(默认行为),或使用稳定的项目 ID 作为键;不要将不同项目合并为共享的存在性键或分支键。在评审中,指出任何捕获外部目标状态的渲染表达式,并建议在该处使用 lambda 的目标值。如果测试必须验证过渡身份,请在重叠期间检查退出和进入的内容;只检查稳定后的目标内容会遗漏共享身份问题。有关状态持有者的详细信息,请参阅 AnimatedContent 身份。
  5. 当动画化的 State 以帧率变化时,将其放在布局或绘制块修饰符中;更深入的诊断请参阅 Compose 性能。
  6. 对于由 Navigation Compose 管理的目标页面切换,使用 Navigation Compose 过渡;对于基于美术素材的动效,使用专用库。
  7. 当 API、生命周期和内容身份都与 UI 相符、没有更简单的适用 API,且相关行为已验证时,即可结束。

API 选择

需求首选
根据进入/退出语义显示或隐藏子树AnimatedVisibility
单个值随状态变化animate*AsState
多个值随同一个布尔值、枚举或密封状态变化rememberTransition 加子动画
子项大小发生变化Modifier.animateContentSize()
不同的可组合树填充同一区域AnimatedContent;简单场景使用 Crossfade
拖动、滑动、处理中断或命令式控制Animatable

当默认动效不合适时,使用 AnimationSpec;当多个动画需要在工具中清晰区分时,为它们指定不同的 label。

val width by animateDpAsState(
    targetValue = if (expanded) 200.dp else 56.dp,
    animationSpec = spring(dampingRatio = 0.7f),
    label = "fabWidth",
)

对于必须保持同步的值,应在同一个 transition 上定义它们,而不是分别调用多个独立的 animate*AsState:

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
}

对于动画填充,当颜色每帧都会更新时,优先使用 drawBehind { drawRect(color.value) },而不是值形式的 background。对于 API 选择不明确的情况,请先参阅官方选择动画 API指南;重复循环使用 rememberInfiniteTransition,可寻址或由测试控制的进度使用 SeekableTransitionState。

不适用此技能的情况

评论

支持 Markdown