很多动画 API 通常接受用于自定义其行为的参数。
使用 AnimationSpec
参数自定义动画
大多数动画 API 允许开发者通过可选的 AnimationSpec
参数来自定义动画规范。
val alpha: Float by animateFloatAsState(
targetValue = if (enabled) 1f else 0.5f,
// Configure the animation duration and easing.
animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing),
label = "alpha"
)
开发者可以使用不同类型的 AnimationSpec
来创建不同类型的动画。
使用 spring
创建基于物理特性的动画
spring
可在起始值和结束值之间创建基于物理特性的动画。它接受 2 个参数:dampingRatio
和 stiffness
。
dampingRatio
定义弹簧的弹性。默认值为 Spring.DampingRatioNoBouncy
。
图 1. 设置不同的弹簧阻尼比。
stiffness
定义弹簧应向结束值移动的速度。默认值为 Spring.StiffnessMedium
。
图 2. 设置不同的弹簧刚度
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = spring(
dampingRatio = Spring.DampingRatioHighBouncy,
stiffness = Spring.StiffnessMedium
),
label = "spring spec"
)
相比基于时长的 AnimationSpec
类型,spring
可以更流畅地处理中断,因为它可以在目标值在动画中变化时保证速度的连续性。spring
用作很多动画 API(如 animate*AsState
和 updateTransition
)的默认 AnimationSpec。
例如,如果我们将 spring
配置应用于以下由用户触摸驱动的动画,那么在动画播放过程中中断动画时,您会发现使用 tween
的响应速度不如使用 spring
的响应速度流畅。
图 3. 为动画设置 tween
和 spring
规范,以及中断动画。
使用 tween
在起始值和结束值之间添加动画效果,并使用缓和曲线
tween
在指定的 durationMillis
内使用缓和曲线在起始值和结束值之间添加动画效果。tween
是“between”(介于)的缩写,因为它介于两个值之间。
您还可以指定 delayMillis
来推迟动画播放的开始时间。
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = tween(
durationMillis = 300,
delayMillis = 50,
easing = LinearOutSlowInEasing
),
label = "tween delay"
)
如需了解详情,请参阅缓动。
使用 keyframes
在特定时间点以动画方式呈现特定值
keyframes
会根据在动画时长内的不同时间戳中指定的快照值添加动画效果。在任何给定时间,动画值都将插值到两个关键帧值之间。对于其中每个关键帧,您都可以指定 Easing 来确定插值曲线。
您可以选择在 0 毫秒和持续时间处指定值。如果不指定这些值,它们将分别默认为动画的起始值和结束值。
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = keyframes {
durationMillis = 375
0.0f at 0 using LinearOutSlowInEasing // for 0-15 ms
0.2f at 15 using FastOutLinearInEasing // for 15-75 ms
0.4f at 75 // ms
0.4f at 225 // ms
},
label = "keyframe"
)
使用 repeatable
重复动画
repeatable
反复运行基于时长的动画(例如 tween
或 keyframes
),直至达到指定的迭代计数。您可以传递 repeatMode
参数来指定动画是从头开始 (RepeatMode.Restart
) 还是从结尾开始 (RepeatMode.Reverse
) 重复播放。
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = repeatable(
iterations = 3,
animation = tween(durationMillis = 300),
repeatMode = RepeatMode.Reverse
),
label = "repeatable spec"
)
使用 infiniteRepeatable
无限重复动画
infiniteRepeatable
与 repeatable
类似,但它会重复无限次的迭代。
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 300),
repeatMode = RepeatMode.Reverse
),
label = "infinite repeatable"
)
在使用 ComposeTestRule
的测试中,使用 infiniteRepeatable
的动画不会运行。系统将使用每个动画值的初始值来呈现组件。
使用 snap
立即跳转到结束值
snap
是特殊的 AnimationSpec
,它会立即将值切换到结束值。您可以指定 delayMillis
来延迟动画播放的开始时间。
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = snap(delayMillis = 50),
label = "snap spec"
)
设置自定义缓动函数
基于时长的 AnimationSpec
操作(如 tween
或 keyframes
)使用 Easing
来调整动画的小数值。这样可让动画值加速和减速,而不是以恒定的速率移动。小数是介于 0(起始值)和 1.0(结束值)之间的值,表示动画中的当前点。
Easing 实际上是一个函数,它取一个介于 0 和 1.0 之间的小数值并返回一个浮点数。返回的值可能位于边界之外,表示过冲或下冲。您可以使用如下所示的代码创建一个自定义 Easing。
val CustomEasing = Easing { fraction -> fraction * fraction }
@Composable
fun EasingUsage() {
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = tween(
durationMillis = 300,
easing = CustomEasing
),
label = "custom easing"
)
// ……
}
Compose 提供多种内置 Easing
函数,可满足大多数用例的需要。如需详细了解根据您的情况应使用哪种 Easing,请参阅速度 - Material Design。
FastOutSlowInEasing
LinearOutSlowInEasing
FastOutLinearEasing
LinearEasing
CubicBezierEasing
- 查看更多
通过转换为和从 AnimationVector
转换来为自定义数据类型添加动画效果
大多数 Compose 动画 API 默认支持将 Float
、Color
、Dp
以及其他基本数据类型作为动画值,但有时您需要为其他数据类型(包括您的自定义类型)添加动画效果。在动画播放期间,任何动画值都表示为 AnimationVector
。使用相应的 TwoWayConverter
即可将值转换为 AnimationVector
,反之亦然,这样一来,核心动画系统就可以统一对其进行处理。例如,Int
表示为包含单个浮点值的 AnimationVector1D
。用于 Int
的 TwoWayConverter
如下所示:
val IntToVector: TwoWayConverter<Int, AnimationVector1D> =
TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })
Color
实际上是 red、green、blue 和 alpha 这 4 个值的集合,因此,Color
可转换为包含 4 个浮点值的 AnimationVector4D
。通过这种方式,动画中使用的每种数据类型都可以根据其维度转换为 AnimationVector1D
、AnimationVector2D
、AnimationVector3D
或 AnimationVector4D
。这样可为对象的不同组件单独添加动画效果,且每个组件都有自己的速度跟踪。您可以使用 Color.VectorConverter
或 Dp.VectorConverter
等转换器访问针对基本数据类型的内置转换器。
如需支持将新的数据类型作为动画值,您可以创建自己的 TwoWayConverter
并将其提供给 API。例如,您可以使用 animateValueAsState
为自定义数据类型添加动画效果,如下所示:
data class MySize(val width: Dp, val height: Dp)
@Composable
fun MyAnimation(targetSize: MySize) {
val animSize: MySize by animateValueAsState(
targetSize,
TwoWayConverter(
convertToVector = { size: MySize ->
// Extract a float value from each of the `Dp` fields.
AnimationVector2D(size.width.value, size.height.value)
},
convertFromVector = { vector: AnimationVector2D ->
MySize(vector.v1.dp, vector.v2.dp)
}
),
label = "size"
)
}
以下列表包含一些内置 VectorConverter
:
Color.VectorConverter
Dp.VectorConverter
Offset.VectorConverter
Int.VectorConverter
Float.VectorConverter
IntSize.VectorConverter