Compose 高级动画
自定义动画
1. 动画规格定制
// 自定义动画规格
val customSpec = spring<Float>(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
@Composable
fun CustomAnimatedButton() {
var selected by remember { mutableStateOf(false) }
val scale by animateFloatAsState(
targetValue = if (selected) 1.2f else 1f,
animationSpec = customSpec
)
Button(
onClick = { selected = !selected },
modifier = Modifier.scale(scale)
) {
Text("弹性按钮")
}
}2. 关键帧动画
@Composable
fun KeyframeAnimation() {
var isAnimating by remember { mutableStateOf(false) }
val offset by animateIntAsState(
targetValue = if (isAnimating) 300 else 0,
animationSpec = keyframes {
durationMillis = 1000
0 at 0 with LinearEasing
150 at 250 with FastOutSlowInEasing
300 at 1000 with LinearEasing
}
)
Box(
modifier = Modifier
.offset(x = offset.dp)
.size(50.dp)
.background(Color.Blue)
.clickable { isAnimating = !isAnimating }
)
}动画状态管理
1. 动画状态机
enum class BoxState { SMALL, LARGE }
@Composable
fun AnimatedBox() {
var boxState by remember { mutableStateOf(BoxState.SMALL) }
val transition = updateTransition(boxState, label = "boxTransition")
val size by transition.animateDp(label = "size") { state ->
when (state) {
BoxState.SMALL -> 50.dp
BoxState.LARGE -> 100.dp
}
}
val color by transition.animateColor(label = "color") { state ->
when (state) {
BoxState.SMALL -> Color.Blue
BoxState.LARGE -> Color.Red
}
}
Box(
modifier = Modifier
.size(size)
.background(color)
.clickable {
boxState = when (boxState) {
BoxState.SMALL -> BoxState.LARGE
BoxState.LARGE -> BoxState.SMALL
}
}
)
}2. 无限动画
@Composable
fun InfiniteAnimation() {
val infiniteTransition = rememberInfiniteTransition()
val color by infiniteTransition.animateColor(
initialValue = Color.Red,
targetValue = Color.Blue,
animationSpec = infiniteRepeatable(
animation = tween(1000),
repeatMode = RepeatMode.Reverse
)
)
Box(
modifier = Modifier
.size(100.dp)
.background(color)
)
}手势动画
1. 拖拽动画
@Composable
fun DraggableAnimation() {
var offsetX by remember { mutableStateOf(0f) }
val draggableState = rememberDraggableState { delta ->
offsetX += delta
}
Box(
modifier = Modifier
.offset { IntOffset(offsetX.roundToInt(), 0) }
.draggable(
state = draggableState,
orientation = Orientation.Horizontal,
onDragStopped = { velocity ->
// 处理惯性滑动
}
)
.size(50.dp)
.background(Color.Green)
)
}2. 手势过渡动画
@Composable
fun SwipeableCard() {
val width = 300.dp
val swipeableState = rememberSwipeableState(0)
val sizePx = with(LocalDensity.current) { width.toPx() }
val anchors = mapOf(0f to 0, sizePx to 1)
Box(
modifier = Modifier
.width(width)
.swipeable(
state = swipeableState,
anchors = anchors,
thresholds = { _, _ -> FractionalThreshold(0.3f) },
orientation = Orientation.Horizontal
)
.background(
color = lerp(
start = Color.White,
stop = Color.Red,
fraction = swipeableState.offset.value / sizePx
)
)
) {
Text(
text = "滑动我",
modifier = Modifier.align(Alignment.Center)
)
}
}最佳实践
-
性能优化
- 避免在动画过程中创建对象
- 使用 remember 缓存动画状态
- 合理设置动画时长和曲线
-
用户体验
- 保持动画流畅性
- 提供适当的视觉反馈
- 考虑无障碍性支持
-
代码组织
- 将动画逻辑封装到独立组件
- 使用状态机管理复杂动画
- 保持动画代码可维护性