Compose开箱即用的动画API

这篇文章介绍一些Compose官方已经封装好的,可以开箱即用的动画API。

animateContentSize内容大小动画

animateContentSize内容大小动画用于修改组件的内容大小时添加动画效果,常用场景为展示内容过多需要收起,点击后再将其显示完整。

fun Modifier.animateContentSize(
    animationSpec: FiniteAnimationSpec<IntSize> = spring(),
    finishedListener: ((initialValue: IntSize, targetValue: IntSize) -> Unit)? = null
): Modifier = composed(
    inspectorInfo = debugInspectorInfo {
        name = "animateContentSize"
        properties["animationSpec"] = animationSpec
        properties["finishedListener"] = finishedListener
    }
) {
    val scope = rememberCoroutineScope()
    val animModifier = remember(scope) {
        SizeAnimationModifier(animationSpec, scope)
    }
    animModifier.listener = finishedListener
    this.clipToBounds().then(animModifier)
}

animateContentSize为Modifier的扩展方法,因此只需要组件的modifier调用该方法,便可在该组件的内容进行变化重组的时候出现该动画。
animateContentSize内含两个参数,都有默认值,第一个参数为animationSpec动画规格,默认为SpringSpec弹性动画,可自定义动画效果,第二个参数为动画完成时的监听,默认为空。
简单示例:

    var change by remember { mutableStateOf(false) }
    Column(modifier = Modifier.animateContentSize()) {
        Text(text = "点击改变内容大小", modifier = Modifier.clickable { change = !change })
        if (change) {
            Text(
                text = "这是一个大方块",
                modifier = Modifier
                    .size(100.dp)
                    .background(Color.Gray),
            )
        }
    }

AnimatedVisibility可见性动画

AnimatedVisibility可见性动画用于组件的出现和消失时添加动画效果。

@Composable
fun AnimatedVisibility(
    visible: Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandIn(),
    exit: ExitTransition = shrinkOut() + fadeOut(),
    label: String = "AnimatedVisibility",
    content: @Composable() AnimatedVisibilityScope.() -> Unit
) {
    val transition = updateTransition(visible, label)
    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content)
}

AnimatedVisibility有6个参数,只有第一个和最后一个参数必须填入,其余参数都有默认值。

  • visible:定义内容是否应该可见,true 为可见,false 为不可见。
  • modifier:修饰符,Compose里随处可见。
  • enter:内容可见时的动画,默认为fadeIn() + expandIn(),大致意思为淡入并扩展开。
  • exit:内容不可见时的动画,默认为shrinkOut() + fadeOut(),大致意思为缩小并淡出消失。
  • label:标签,用于区分不同动画。
  • content:添加可见性动画效果的组合项内容。

简单示例:

    var change by remember { mutableStateOf(false) }
    Column {
        Text(text = "点击改变可见性", modifier = Modifier.clickable { change = !change })
        AnimatedVisibility(visible = change) {
            Text(
                text = "这是一个大方块",
                modifier = Modifier
                    .size(100.dp)
                    .background(Color.Gray),
            )
        }
    }

当然可完全可以自定义可见/不可见时的动画效果:

    var change by remember { mutableStateOf(false) }
    Column {
        Text(text = "点击改变可见性", modifier = Modifier.clickable { change = !change })
        AnimatedVisibility(
            visible = change,
            enter = slideInVertically(
                initialOffsetY = { fullHeight -> -fullHeight },
                animationSpec = tween(durationMillis = 150, easing = LinearOutSlowInEasing)
            ),
            exit = slideOutVertically(
                targetOffsetY = { fullHeight -> -fullHeight },
                animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing)
            )
        ) {
            Text(
                text = "这是一个大方块",
                modifier = Modifier
                    .size(100.dp)
                    .background(Color.Gray),
            )
        }
    }

animateValueAsState单一值属性动画

属性动画就是通过不断地修改属性值来实现的。animateValueAsState对任意值的属性动画。官方提供了一整套的api来实现简单的单一值属性动画。
image.png
实际使用的时候按需挑选即可。

@Composable
fun animateDpAsState(
    targetValue: Dp,
    animationSpec: AnimationSpec<Dp> = dpDefaultSpring,
    label: String = "DpAnimation",
    finishedListener: ((Dp) -> Unit)? = null
): State<Dp> {
    return animateValueAsState(
        targetValue,
        Dp.VectorConverter,
        animationSpec,
        label = label,
        finishedListener = finishedListener
    )
}

以animateDpAsState为例,共4个参数,第一个为目标值,必须填入,其余参数都有默认值,第二个参数为animationSpec动画规格,默认为SpringSpec弹性动画,可自定义动画效果,第三第四个参数前面出现过,按字面意思也可理解就不细说了。整个动画会以当前值为开始值,传入的targetValue为结束值,以animationSpec动画规格进行动画效果。
简单示例:

    var small by remember { mutableStateOf(true) }
    val size: Dp by animateDpAsState(
        targetValue = if (small) 40.dp else 100.dp
    )
    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(onClick = { small = !small }) {
            Text(text = "改变方块大小")
        }
        Box(
            modifier = Modifier
                .size(size)
                .background(Color.LightGray)
        )
    }

updateTransition多值动画

updateTransition可以保存一个或多个子动画,当状态发生改变时,多个动画同时进行。可以使用animate*扩展方法添加子动画。
简单示例:使用animateDp和 animateColor同时进行两个子动画

    var change by remember { mutableStateOf(false) }
    val transition = updateTransition(targetState = change, label = "多值动画")
    val offset by transition.animateDp(label = "") { change ->
        if (change) 50.dp else 0.dp
    }
    val background by transition.animateColor(label = "") { change ->
        if (change) Color.Gray else Color.Blue
    }
    Column(modifier = Modifier.fillMaxWidth()) {
        Text(text = "点击进行多个动画", modifier = Modifier.clickable { change = !change })
        Text(
            text = "这是一个大方块",
            modifier = Modifier
                .size(100.dp)
                .offset(x = offset)
                .background(background),
        )
    }

rememberInfiniteTransition无限重复动画

rememberInfiniteTransition可以进行无限重复动画,也一样可以保存一个或多个子动画,但是,这些动画一进入组合阶段就开始运行,除非被移除,否则不会停止,可以使用 animateColor、animatedFloat 或 animatedValue 添加子动画。
简单示例:使用animatedValue添加子动画改变大小size,并且使用animatedFloat添加子动画改变大小透明度alpha

    val infiniteTransition = rememberInfiniteTransition()
    val size by infiniteTransition.animateValue(
        initialValue = 100.dp,
        targetValue = 200.dp,
        typeConverter = TwoWayConverter({ AnimationVector1D(it.value) }, { it.value.dp }),
        animationSpec = infiniteRepeatable(
            animation = tween(1000, easing = LinearEasing),
            repeatMode = RepeatMode.Reverse
        )
    )
    val alpha by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(
            animation = tween(1000, easing = LinearEasing),
            repeatMode = RepeatMode.Reverse
        )
    )

    Box(
        Modifier
            .size(size)
            .padding(20.dp)
            .alpha(alpha)
            .background(Color.Red)
    )

最后

最后,使用上面这些动画API可以达到一些简单的动画效果,但如果想要一些复杂的自定义的动画效果,可以使用animationSpec这个参数,具体见Compose动画学习之AnimationSpec

猜你喜欢

转载自blog.csdn.net/yuantian_shenhai/article/details/128164793