Compose out-of-the-box animation API

This article introduces some animation APIs that Compose has officially packaged and can be used out of the box.

animateContentSize content size animation

animateContentSize content size animation is used to add animation effects when modifying the content size of the component. The common scenario is to display too much content and need to put it away, and then click to display it completely.

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 is an extension method of Modifier, so only the modifier of the component needs to call this method, and the animation can appear when the content of the component is changed and reorganized.
animateContentSize contains two parameters, both of which have default values. The first parameter is the animationSpec animation specification. The default is SpringSpec elastic animation, and the animation effect can be customized. The second parameter is the monitor when the animation is completed, and it is empty by default.
Simple example:

    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 visibility animation

The AnimatedVisibility visibility animation is used to animate the appearance and disappearance of components.

@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 has 6 parameters, only the first and last parameters must be filled in, and the rest of the parameters have default values.

  • visible: defines whether the content should be visible, true is visible, false is invisible.
  • modifier: Modifiers can be seen everywhere in Compose.
  • enter: The animation when the content is visible, the default is fadeIn() + expandIn(), roughly meaning to fade in and expand.
  • exit: The animation when the content is invisible, the default is shrinkOut() + fadeOut(), which roughly means shrinking and fading out and disappearing.
  • label: label, used to distinguish different animations.
  • content: The content of the combined item to which the visibility animation effect is added.

Simple example:

    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),
            )
        }
    }

Of course, you can completely customize the animation effect when it is visible/invisible:

    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 single value property animation

Property animation is achieved by continuously modifying property values. animateValueAsState animates properties with arbitrary values. The official provides a complete set of APIs to implement simple single-value property animations.
image.png
In actual use, you can choose according to your needs.

@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
    )
}

Take animateDpAsState as an example, there are 4 parameters in total, the first is the target value, which must be filled in, and the rest of the parameters have default values, the second parameter is the animationSpec animation specification, the default is SpringSpec elastic animation, and the animation effect can be customized. The third and fourth parameters have appeared before, and they can be understood literally, so I won’t go into details. The entire animation will use the current value as the start value, the passed in targetValue as the end value, and the animation effect will be performed according to the animationSpec animation specification.
Simple example:

    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 multi-valued animation

updateTransition can save one or more sub-animations, and when the state changes, multiple animations are performed at the same time. Child animations can be added using the animate* extension methods.
Simple example: use animateDp and animateColor to make two child animations at the same time

    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 infinite repeat animation

rememberInfiniteTransition can perform infinite repeating animations, and can also save one or more sub-animations. However, these animations start running as soon as they enter the composition phase, and will not stop unless they are removed. You can use animateColor, animatedFloat or animatedValue to add sub-animations.
Simple example: use animatedValue to add a sub-animation to change the size, and use animatedFloat to add a sub-animation to change the size and transparency 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)
    )

at last

Finally, using the above animation APIs can achieve some simple animation effects, but if you want some complex custom animation effects, you can use the animationSpec parameter. For details, see AnimationSpec of Compose animation learning .

Guess you like

Origin blog.csdn.net/yuantian_shenhai/article/details/128164793