⚡ animateValueAsState 是 Compose 中面向 任意自定义类型 的动画驱动 API。
基于 TwoWayConverter 将任意类型与 AnimationVector 互转,实现 Int、Float、Offset 乃至自定义类型的平滑过渡。
@Composable
fun <T> animateValueAsState(
targetValue: T,
typeConverter: TwoWayConverter<T, AnimationVector>,
animationSpec: AnimationSpec<T> = tween(),
label: String = "",
finishedListener: ((T) -> Unit)? = null
): State<T>
State<T>,重组时自动更新将任意类型 T 映射为 AnimationVector (1~4维向量)
val FloatToVector: TwoWayConverter<Float, AnimationVector1D> =
TwoWayConverter(
convertToVector = { AnimationVector1D(it) },
convertFromVector = { it.value }
)
系统内置 Int.VectorConverter, Float.VectorConverter, Offset.VectorConverter 等。
假设我们有一个 BoxSize 包含宽、高、圆角,需要同时动画:
data class BoxSize(val width: Float, val height: Float, val radius: Float)
val BoxSizeConverter = TwoWayConverter<BoxSize, AnimationVector3D>(
convertToVector = { size ->
AnimationVector3D(size.width, size.height, size.radius)
},
convertFromVector = { vector ->
BoxSize(vector.v1, vector.v2, vector.v3)
}
)
// 使用
var big by remember { mutableStateOf(false) }
val size by animateValueAsState(
targetValue = if (big) BoxSize(200f, 200f, 40f)
else BoxSize(100f, 100f, 12f),
typeConverter = BoxSizeConverter,
animationSpec = tween(600, easing = FastOutSlowInEasing)
)
✅ 任意维度数据 (1~4) 均可通过 AnimationVector1D~4D 实现平滑过渡。
| 类型 | 转换器 (内置) | 动画规格示例 | 典型场景 |
|---|---|---|---|
| Int | Int.VectorConverter |
tween(500) / spring() |
进度值、页码、徽标计数 |
| Float | Float.VectorConverter |
tween(800, easing = LinearEasing) |
透明度、旋转角度、缩放 |
| Offset | Offset.VectorConverter |
spring(dampingRatio = 0.5f) |
位置移动、拖拽偏移、滑动 |
var count by remember { mutableStateOf(0) }
val animatedCount by animateValueAsState(
targetValue = count,
typeConverter = Int.VectorConverter,
animationSpec = tween(400)
)
// Text(text = animatedCount.value.toString())
var alpha by remember { mutableStateOf(1f) }
val animatedAlpha by animateValueAsState(
targetValue = alpha,
typeConverter = Float.VectorConverter,
animationSpec = spring(
dampingRatio = 0.3f,
stiffness = 200f
)
)
// Box(modifier = Modifier.alpha(animatedAlpha.value))
var offset by remember { mutableStateOf(Offset.Zero) }
val animatedOffset by animateValueAsState(
targetValue = offset,
typeConverter = Offset.VectorConverter,
animationSpec = tween(600, easing = FastOutSlowInEasing)
)
// Box(modifier = Modifier.offset { animatedOffset.value.toIntOffset() })
💡 Offset.VectorConverter 自动将 Offset 映射为 AnimationVector2D (x, y)。
TwoWayConverter,维度 ≤ 4Int.VectorConverter (四舍五入)spring() 实现弹性效果Modifier.offsetfinishedListener 监听动画结束回调targetValue 导致中断Animatable 可手动控制更精细的动画@Composable
fun MultiValueAnimation() {
var enabled by remember { mutableStateOf(false) }
val intValue by animateValueAsState(
targetValue = if (enabled) 100 else 0,
typeConverter = Int.VectorConverter,
animationSpec = tween(600)
)
val floatValue by animateValueAsState(
targetValue = if (enabled) 1f else 0.3f,
typeConverter = Float.VectorConverter,
animationSpec = spring(dampingRatio = 0.5f)
)
var offsetTarget by remember { mutableStateOf(Offset.Zero) }
val offsetValue by animateValueAsState(
targetValue = offsetTarget,
typeConverter = Offset.VectorConverter,
animationSpec = tween(400)
)
// 组合使用 intValue, floatValue, offsetValue 驱动UI
Box(
modifier = Modifier
.offset { offsetValue.value.toIntOffset() }
.alpha(floatValue.value)
.size(intValue.value.dp)
.background(Color(0xFFF7B731))
)
}