本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一、开箱即用的高级别动画Api
动画 | 描述 | 部分参数解释 |
---|---|---|
AnimatedVisibility | 为其内容的出现和消失添加动画效果 | visible:显示/隐藏状态,改变时触发动画。 enter:指定进入动画效果。 exit:指定退出动画效果。 |
AnimatedContent | 在内容根据目标状态发生变化时,为内容添加动画效果 | targetState:目标状态,改变时触发动画。 transitionSpec:用于指定内容切换时的动画。 |
animateContentSize | 为大小变化添加动画效果 | animationSpec:动画规范,默认为sping() 。可选项包含TweenSpec 、SpringSpec 、KeyframesSpec 、RepeatableSpec 、SnapSpec 。finishedListener:动画完成后的监听。 |
Crossfade | 在两个布局之间切换时添加淡入淡出动画 | targetState:目标状态,改变时触发动画。 animationSpec:动画。默认为 tween(Int, Int, Easing) ,参数分别为持续时长、延迟时长、从开始到结束的差值器。 |
1、AnimatedVisibility:为内容的出现和消失添加动画
系统提供了开箱即用的八种进入动画EnterTransition
和八种退出动画ExitTransition
。要同时使用多种动画效果,直接通过加号(+)进行组合即可,如:fadeIn() + slideInVertically()
。
八种进入动画 | 预览 | 描述及部分参数解释 |
---|---|---|
fadeIn | 淡入 initialAlpha:动画开始时的透明度 |
|
slideIn | 滑入 initialOffset:动画开始时位置的偏移量 |
|
slideInHorizontally | 水平滑入 initialOffsetX:动画开始时位置的横向偏移量 |
|
slideInVertically | 垂直滑入 initialOffsetY:动画开始时位置的垂直偏移量 |
|
scaleIn | 缩放进入 initialScale:动画开始时的缩放比例 transformOrigin:动画执行的基准点 |
|
expandIn | 展开进入 expandFrom:从原始样式的哪个方向开始执行动画 clip:动画执行时,是否剪切掉原始组件空间外的内容 initialSize:动画开始时的初始尺寸 |
|
expandHorizontally | 水平展开进入 expandFrom:从原始样式的哪个方向开始执行动画 clip:动画执行时,是否剪切掉原始组件空间外的内容 initialWidth:动画开始时的初始宽度 |
|
expandVertically | 垂直展开进入 expandFrom:从原始样式的哪个方向开始执行动画 clip:动画执行时,是否剪切掉原始组件空间外的内容 initialHeight:动画开始时的初始高度 |
八种退出动画 | 预览 | 描述及部分参数解释 |
---|---|---|
fadeOut | 淡出 targetAlpha:动画目标的透明度 |
|
slideOut | 滑出 targetOffset:动画目标位置的偏移量 |
|
slideOutHorizontally | 水平滑出 targetOffsetX:动画目标位置的横向偏移量 |
|
slideOutVertically | 垂直滑出 targetOffsetY:动画目标位置的垂直偏移量 |
|
scaleOut | 缩放进入 targetScale:动画目标的缩放比例 transformOrigin:动画执行的基准点 |
|
shrinkOut | 收缩退出 shrinkTowards:缩小范围的终点 clip:动画执行时,是否剪切掉原始组件空间外的内容 targetSize:动画结束的终止尺寸 |
|
shrinkHorizontally | 水平收缩退出 shrinkTowards:缩小范围的终点 clip:动画执行时,是否剪切掉原始组件空间外的内容 targetWidth:动画结束的终止宽度 |
|
shrinkVertically | 垂直收缩退出 shrinkTowards:缩小范围的终点 clip:动画执行时,是否剪切掉原始组件空间外的内容 targetHeight:动画结束的终止高度 |
2、AnimatedContent:在内容发生变化时添加动画
参数 | 说明 | 解释 |
---|---|---|
targetState | 目标状态 | 该状态变化时,将触发动画 |
transitionSpec | 动画行为 | 可通过with infix 来组合进入动画与退出动画 |
content | 内容 | 使用目标状态的值,接收动画的触发条件 |
3、animateContentSize:为大小变化添加动画
只需要给modifier
多配置一项animateContentSize()
,即可开启该动画。举例:
var large by remember { mutableStateOf(true) }
Button(onClick = { large = !large }) {
Text(text = if (large) "变短" else "变长")
}
Box(
modifier = Modifier
.padding(top = 4.dp)
.background(Color.Blue)
.animateContentSize()
) {
Text(
text = "Hello",
modifier = Modifier.width(if (large) 100.dp else 50.dp)
)
}
复制代码
4、Crossfade:在布局间切换时添加淡入淡出动画
参数 | 说明 | 解释 |
---|---|---|
targetState | 目标状态 | 该状态变化时,将触发动画 |
animationSpec | 动画配置 | 默认为tween() |
content | 内容 | 使用目标状态的值,接收动画的触发条件 |
举例:
var currentPage by remember { mutableStateOf("A") }
Button(onClick = {
currentPage = if (currentPage == "A") {
"B"
} else {
"A"
}
}) {
Text(text = if (currentPage == "A") "切换到B" else "切换到A")
}
Crossfade(
targetState = currentPage,
modifier = Modifier.padding(top = 4.dp),
animationSpec = tween(durationMillis = 1500)
) { screen ->
when (screen) {
"A" -> Box(
modifier = Modifier
.size(100.dp)
.background(color = Color.Cyan)
) { Text("Page A") }
"B" -> Box(
modifier = Modifier.size(100.dp)
) { Text("Page B") }
}
}
复制代码
二、开箱即用的低级别动画
1、animate*AsState:为单个值添加动画效果
只需提供一个目标值,该 API 就会从当前值开始向目标值播放动画。
Column {
val enabled = remember { mutableStateOf(true) }
val alpha: Float by animateFloatAsState(if (enabled.value) 1f else 0.5f)
Box(
modifier = Modifier
.padding(vertical = 10.dp)
.width(width = 100.dp)
.height(height = 50.dp)
.graphicsLayer(alpha = alpha)
.background(color = Color.Red)
)
Button(onClick = { enabled.value = !enabled.value }) {
Text(text = if (enabled.value) "切换为不可用" else "切换为可用")
}
}
复制代码
上面的列子使用了animateFloatAsState
来为透明度alpha
添加了动画效果。包含Float
在内,Compose 为 Float
、Color
、Dp
、Size
、Offset
、Rect
、Int
、IntOffset
和 IntSize
提供开箱即用的 animate*AsState
函数。另外,Compose还提供了一个animateValueAsState
函数,支持对任何其他数据类型的支持。
2、Animatable
这种动画除了支持实现上面的animate*AsState
的效果之外,还可以对内容做更加精细的控制。比如:
- 初始值可以与目标值不同
animateTo
可以在值的变化过程中使用动画,snapTo
则可以让当前值立即变为目标值
Animatable
的使用步骤如下:
- 创建一个
Animatable
对象。 - 开启动画作用域。
- 在组件中使用
Animatable
的值。
Column {
//控制尺寸大小
var big by remember { mutableStateOf(false) }
val sizeAnimatable = remember { Animatable(32.dp, Dp.VectorConverter) }
//控制颜色
var colorTag by remember { mutableStateOf(1) }
val colorAnimatable = remember {
//初始值设置为黄色
Animatable(initialValue = Color.Yellow)
}
//是否开启动画作用域
val animateOpened = remember { mutableStateOf(false) }
if (animateOpened.value) {
LaunchedEffect(key1 = big) {
// 以动画的过程对 size 的值进行切换
sizeAnimatable.animateTo(targetValue = if (big) 144.dp else 32.dp)
}
LaunchedEffect(key1 = colorTag) {
// 以动画的过程对 color 的值进行切换
colorAnimatable.animateTo(
targetValue = when (colorTag) {
1 -> Color.Green
2 -> Color.Red
else -> Color.Blue
}
)
}
}
Box(modifier = Modifier
.size(size = sizeAnimatable.value) // size 在 sizeAnimate 中取值
.background(color = colorAnimatable.value) // color 在 colorAnimate 中取值
.clickable { animateOpened.value = !animateOpened.value }
)
Text(text = "点击上图开关动画功能,当前已${if (animateOpened.value) "开启" else "关闭"}")
Row {
Button(onClick = { big = true }) {
Text(text = "变大")
}
Button(onClick = { big = false }) {
Text(text = "变小")
}
}
Row(modifier = Modifier.padding(top = 4.dp)) {
Button(onClick = { colorTag = 1 }) {
Text(text = "变成绿色")
}
Button(onClick = { colorTag = 2 }) {
Text(text = "变成红色")
}
Button(onClick = { colorTag = 3 }) {
Text(text = "变成蓝色")
}
}
}
复制代码
3、updateTransition
Transition
可管理一个或多个动画作为其子项,并在多个状态切换的过程中同时运行这些动画。
Transition
的使用步骤如下:
- 创建一组状态值。为了保证状态值的范围性,一般可选用枚举来定义。
- 定义一组此过渡效果中的子动画。
- 在组件中使用这些属性的值。
下面的列子还是实现的上面讲的Animatable
部分的效果。
enum class BoxState {
BIG_GREEN, // 大-绿色
BIG_RED, // 大-红色
BIG_BLUE, // 大-蓝色
SMALL_GREEN, // 小-绿色
SMALL_RED, // 小-红色
SMALL_BLUE // 小-蓝色
}
@Composable
fun UpdateTransitionCode() {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
//定义初始状态为 小-绿色
var currentState by remember { mutableStateOf(BoxState.SMALL_GREEN) }
//创建 Transition
val transition = updateTransition(targetState = currentState, label = null)
//定义 尺寸 子动画
val sizeTransition by transition.animateDp(label = "") { state ->
when (state) {
BoxState.BIG_GREEN,
BoxState.BIG_RED,
BoxState.BIG_BLUE -> 144.dp
BoxState.SMALL_GREEN,
BoxState.SMALL_RED,
BoxState.SMALL_BLUE -> 32.dp
}
}
//定义 颜色 子动画
val colorTransition by transition.animateColor(label = "") { state ->
when (state) {
BoxState.BIG_GREEN,
BoxState.SMALL_GREEN -> Color.Green
BoxState.BIG_RED,
BoxState.SMALL_RED -> Color.Red
BoxState.BIG_BLUE,
BoxState.SMALL_BLUE -> Color.Blue
}
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Box(
modifier = Modifier
.size(size = sizeTransition) // size 取 sizeTransition 的值
.background(color = colorTransition) // color 取 colorTransition 的值
)
Row(modifier = Modifier.padding(top = 4.dp)) {
Button(onClick = { currentState = BoxState.BIG_GREEN }) {
Text(text = "大-绿色")
}
Button(onClick = { currentState = BoxState.BIG_RED }) {
Text(text = "大-红色")
}
Button(onClick = { currentState = BoxState.BIG_BLUE }) {
Text(text = "大-蓝色")
}
}
Row(modifier = Modifier.padding(top = 4.dp)) {
Button(onClick = { currentState = BoxState.SMALL_GREEN }) {
Text(text = "小-绿色")
}
Button(onClick = { currentState = BoxState.SMALL_RED }) {
Text(text = "小-红色")
}
Button(onClick = { currentState = BoxState.SMALL_BLUE }) {
Text(text = "小-蓝色")
}
}
}
}
}
复制代码
4、rememberInfiniteTransition
InfiniteTransition
可用来构建一个无限循环运行的动画,并可以像 Transition
一样保存一个或多个子动画。但是这些动画将会在界面的组合阶段就开始运行,只要不被移除,此动画将永远地运行下去。
使用步骤如下:
- 创建
InfiniteTransition
。 - 利用
InfiniteTransition.animateXxx
API 创建一组子动画。 - 在组件中使用这些属性的值。
// 创建 InfiniteTransition
val infiniteTransition = rememberInfiniteTransition()
// 定义 尺寸 子动画
val sizeState by infiniteTransition.animateFloat(
initialValue = 144f, targetValue = 32f,
animationSpec = infiniteRepeatable(
animation = tween(2000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
// 定义 颜色 子动画
val colorState by infiniteTransition.animateColor(
initialValue = Color.Red,
targetValue = Color.Green,
animationSpec = infiniteRepeatable(
animation = tween(2000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
Box(
modifier = Modifier
.size(size = Dp(sizeState)) // 使用 sizeState
.background(color = colorState) // 使用 colorState
)
复制代码