Node: This article is based on Jetpack Compose 1.0.0-beta01

1. Animation is state-driven

Compose’s core state of mind drives UI refreshes, and the same idea applies to animations.

UI = f(state)

The Compose animation is composed to refresh the UI by constantly evaluating the latest state value. This is similar to traditional ValueAnimator, which calculates the current value based on the interpolator and estimator of the animation and maps to the corresponding properties of the View. Compose is naturally state-driven, making its apis simpler and more reasonable.

2. AnimateAsState: Property animation

AnimateAsState provides traditional property animation capabilities. Click Button to change the color of Box

@Preview
@Composable
fun AnimateAsStateDemo(a) {
    var blue by remember { mutableStateOf(true)}val color = if (blue) Blue else Red,

    Column(Modifier.padding(16.dp)) {
        Text("AnimateAsStateDemo")
        Spacer(Modifier.height(16.dp)) Button( onClick = { blue = ! blue } ) { Text("Change Color")
        }
        Spacer(Modifier.height(16.dp))
        Box(
            Modifier
                .preferredSize(128.dp)
                .background(color)
        )
    }
}
Copy the code

If you want to animate the Color, you can use animateColorAsState

@Composable
fun AnimateAsStateDemo(a) {
    var blue by remember { mutableStateOf(true)}val color by animateColorAsState(
        if (blue) Blue else Red,
        animationSpec = spring(Spring.StiffnessVeryLow)
    )

 	/ /...
}
Copy the code

AnimateColorAsState converts Color changes into a subscriptable state; AnimationSpec is used for animation configuration, such as the example of a spring animation

Animationword could also listen for callbacks at the end of an animation

val color by animateColorAsState(
    if (blue) Blue elseRed, animationSpec = spring(stiffness = Spring.StiffnessVeryLow), finishedListener = { blue = ! blue } )Copy the code

As above, you can achieve the effect of backanimation

In addition to AnimateColorAsState, various other types of animations are supported:

UpdateTransition: Synchronize multiple animations

If you want to animate multiple properties simultaneously and keep them in sync, you need to use updateTransition, similar to using AnimationSet to combine multiple animations


private sealed class BoxState(val color: Color, val size: Dp) {
    operator fun not(a) = if (this is Small) Large else Small
    object Small : BoxState(Blue, 64.dp)
    object Large : BoxState(Red, 128.dp)
}

@Composable
fun UpdateTransitionDemo(a) {

    var boxState: BoxState by remember { mutableStateOf(BoxState.Small) }
    val transition = updateTransition(targetState = boxState)

    Column(Modifier.padding(16.dp)) {
        Text("UpdateTransitionDemo")
        Spacer(Modifier.height(16.dp))

        val color by transition.animateColor {
            boxState.color
        }
        val size by transition.animateDp(transitionSpec = {
            if (targetState == BoxState.Large) {
                spring(stiffness = Spring.StiffnessVeryLow)
            } else{ spring(stiffness = Spring.StiffnessHigh) } }) { boxState.size } Button( onClick = { boxState = ! boxState } ) { Text("Change Color and size")
        }
        Spacer(Modifier.height(16.dp))
        Box(
            Modifier
                .preferredSize(size)
                .background(color)
        )
    }
}
Copy the code

UpdateTransition creates a Transition based on targetState, and you can then create the states required by the various property animations using the Transition extension function.

It’s important to note that the Transition codename is confused with a traditional shift animation, but it’s not related at all. Here, the Transition codename is a tool for synchronizing multiple animations.

The transitionSpec can be animated. In the example above, the Box is zoomed in and out with different elastic animation effects.

Also, Transition has multiple extension functions, depending on the type of attribute

4. AnimateVisibility: Visibility animation

Animating a View when its visibility changes is a common requirement. In traditional View architecture, fadeIn/fadeOut is implemented using alpha changes, or slideIn/slideOut is implemented using transitionX/Y changes

AnimatedVisibility is used in Compose

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AnimateVisibilityDemo(a) {
    var visible by remember { mutableStateOf(true) }

    Column(Modifier.padding(16.dp)) {
        Text("AnimateVisibilityDemo")
        Spacer(Modifier.height(16.dp)) Button( onClick = { visible = ! visible } ) { Text(text =if (visible) "Hide" else "Show")
        }

        Spacer(Modifier.height(16.dp))

        AnimatedVisibility(visible) {
            Box(
                Modifier
                    .preferredSize(128.dp)
                    .background(Blue)
            )
        }
    }
}
Copy the code

As above, the default visibility animation is fade in/out + shrink/enlarge:

5. AnimateContentSize: Layout size animation

AnimateContentSize is an extension of the Modifier method. Adding the Modifier to the Composable of this method listens for changes in the Composable size of its children and animates them accordingly

@Composable
fun AnimateContentSizeDemo(a) {
    var expend by remember { mutableStateOf(false) }

    Column(Modifier.padding(16.dp)) {
        Text("AnimateContentSizeDemo")
        Spacer(Modifier.height(16.dp)) Button( onClick = { expend = ! expend } ) { Text(if (expend) "Shrink" else "Expand")
        }
        Spacer(Modifier.height(16.dp))

        Box(
            Modifier
                .background(Color.LightGray)
                .animateContentSize()
        ) {
            Text(
                text = "animateContentSize() animates its own size when its child modifier (or the child composable if it is already at the tail of the chain) changes size. " +
                        "This allows the parent modifier to observe a smooth size change, resulting in an overall continuous visual change.",
                fontSize = 16.sp,
                textAlign = TextAlign.Justify,
                modifier = Modifier.padding(16.dp),
                maxLines = if (expend) Int.MAX_VALUE else 2)}}}Copy the code

AnimateContentSize can also be configured with animSpec and endListener:

Crossfade: Layout switch animation

The Crossfade itself is a Composable that adds a fade in and out effect when the sub-layouts inside it are switched:

@Composable
fun CrossfadeDemo(a) {

    var scene by remember { mutableStateOf(DemoScene.Text) }

    Column(Modifier.padding(16.dp)) {

        Text("AnimateVisibilityDemo")
        Spacer(Modifier.height(16.dp))

        Button(onClick = {
            scene = when (scene) {
                DemoScene.Text -> DemoScene.Icon
                DemoScene.Icon -> DemoScene.Text
            }
        }) {
            Text("toggle")
        }

        Spacer(Modifier.height(16.dp))

        Crossfade(
            current = scene,
            animation = tween(durationMillis = 1000)) {when (scene) {
                DemoScene.Text ->
                    Text(text = "Phone", fontSize = 32.sp)
                DemoScene.Icon ->
                    Icon(
                        imageVector = Icons.Default.Phone,
                        null,
                        modifier = Modifier.preferredSize(48.dp)
                    )
            }
        }

    }
}
Copy the code

reference

Sample Code