Visibility animation

Visibility animation refers to having a transition effect when the View’s visibility changes.

@Composable
fun ColumnScope.AnimatedVisibility(
    visible: Boolean.// Whether it is currently visible
    modifier: Modifier = Modifier, // Layout modifiers
    enter: EnterTransition = fadeIn() + expandVertically(), // The animation when invisible becomes visible expands and fades in by default
    exit: ExitTransition = fadeOut() + shrinkVertically(), // The animation when visible becomes invisible, collapses and fades out by default
    content: @Composable AnimatedVisibilityScope.() -> Unit // What to show
) 
Copy the code

The EnterTransition and ExitTransition operators overload the plus() operator so that + can be used directly to merge multiple animations, such as expand + fade.

For the most part, we don’t need to write complex animations by hand, but Compose provides several built-in animations that we can use on demand. You can see Compose’s default entry and exit animations in the enterexitTransition. kt file.

Now let’s use it:

@Composable
fun VisibilityAnimation(a) {
    val visible = remember { mutableStateOf(true) }
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(10.dp) ) { Button(onClick = { visible.value = ! visible.value }) { Text(text ="Visibility animation")}// 1 Default animation, fade in and out
        AnimatedVisibility(visible = visible.value) {
            Text(text = "Yu Zhanggujun, Hongdu new house, star wing Jane, henglu, front three rivers with five lakes, control pretty jing and ou yue.", modifier = Modifier.size(150.dp))
        }

        // 2 slide in and out horizontally
        AnimatedVisibility(
            visible = visible.value,
            enter = slideInHorizontally(initialOffsetX = { -it }), // Enter animation
            exit = slideOutHorizontally(targetOffsetX = { -it })// Start animation
        ) {
            Text(text = "Yu Zhanggujun, Hongdu new house, star wing Jane, henglu, front three rivers with five lakes, control pretty jing and ou yue.", modifier = Modifier.size(150.dp))
        }
    }
}
Copy the code

The effect is as follows:

Layout size animation

Layout size animation refers to: when the layout size changes anyway, there is a transition effect. Let’s first look at the effect without animation:

The code is as follows:

@Composable
fun LayoutChangeAnimation(a) {
    val expand = remember { mutableStateOf(false) }
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(10.dp)
    ) {
        Text(
            // A long text
            text = "Yuzhang old county, Hongdu new house. Star wing Zhen, henglu to connect. Jin three rivers and with five lakes, control pretty jing and ou yue. Wuhua Tianbao, dragon Guang shooting bull fighting market;" +
                    Xu Ru came down to Chen Fan's bed. Xiongzhou fog column, handsome stars chi. Taiwan god pillow yi Xia, guests and hosts to the southeast of the United States. Governor Yan Gong's ya wang, honor halberd yao Lin; +
                    "Yuwen Xinzhou yi Fan, 襜 curtain temporarily. Ten days off, friends like clouds; Thousands of miles to meet, friends full. Teng Jiao Qifeng, Meng's Ci Zong; Purple, electric and green, the Arsenal of the King's generals." +
                    "Home king for slaughter, road famous area; How does a boy know?",
            modifier = Modifier
                .padding(10.dp),
                //.animatecontentSize (), // The key property, animates the content changes
            maxLines = if (expand.value) Int.MAX_VALUE else 1 // Set the maximum number of rows depending on whether to expand
        )

        // Click "expand "/" collapse"Button(onClick = { expand.value = ! expand.value }) { Text(if (expand.value) "Fold" else "A")}}}Copy the code

Then to add animation, we open this line comment to get the animation to execute:

.animateContentSize(), // Add animation to content changes for key attributes
Copy the code

The effect is as follows:

The animateContentSize API is as follows:

fun Modifier.animateContentSize(
    animationSpec: FiniteAnimationSpec<IntSize> = spring().// Animation properties
    finishedListener: ((initialValue: IntSize, targetValue: IntSize) -> Unit)? = null // 
)
Copy the code

Instead of delving into the FiniteAnimationSpec API, we’ll look directly at the Spring () function provided by Compose, which is sufficient for our needs.

@Stable
fun <T> spring(
    dampingRatio: Float = Spring.DampingRatioNoBouncy, // The damping coefficient, the smaller the value, the greater the spring, the more obvious the spring effect, default no spring
    stiffness: Float = Spring.StiffnessMedium, // The larger the value, the faster the decay, i.e. the faster the folding speed, the default decay coefficient is "medium"
    visibilityThreshold: T? = null // Visibility threshold
)
Copy the code

Now, to change the damping value, we modify animateContentSize to the following code:

.animateContentSize(
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioHighBouncy, // Change to elastic)),// Add animation to content changes for key attributes
Copy the code

The effect is as follows:

We changed the elastic coefficient to be strong, which means the damping coefficient is the lowest, and found that the elastic is more powerful.

Next, let’s change the value of the attenuation coefficient. Let’s change the animateContentSize to the following code:

.animateContentSize(
    animationSpec = spring(
        stiffness = Spring.StiffnessVeryLow // Change the attenuation coefficient to the lowest)),// Add animation to content changes for key attributes
Copy the code

Change the attenuation coefficient to the lowest, you will find that the attenuation is slow, that is, the animation events become longer.

What if we combined these two changes? As follows:

.animateContentSize(
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioHighBouncy, // Change the elastic coefficient to high
        stiffness = Spring.StiffnessVeryLow // Change the attenuation coefficient to the lowest)),// Add animation to content changes for key attributes
Copy the code

It should be more elastic and decay slowly. Let’s take a look at the effect:

It’s exactly what we expected: more elasticity, slower decay. The reason why the bounce is stuck here is because the amplitude is too large, which leads to mutual extrusion. In practical application, the elastic value can be adjusted to solve the problem, so it is unnecessary to care about here.

Layout Switch animation

Layout switch animation refers to adding a transition effect while switching layouts. Let’s take a look at the effects without animation:

As you can see, when there is no animation, the switch is very stiff, just like a flash, and the corresponding code is as follows:

/** * Layout switch animation */
@Composable
fun LayoutSwitchAnimation(a) {
    var first by remember { mutableStateOf(true) }
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp) ) { Button(onClick = { first = ! first }, modifier = Modifier.padding(bottom =10.dp)) { // Click to toggle first to change the layout of the display
            Text(text = "Switch")}// Select whether to display the first screen based on Boolean values
        if (first) {
            FirstScreen()
        } else {
            OtherScreen()
        }
    }
}

// The first screen content
@Composable
fun FirstScreen(a) {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Yellow),
        contentAlignment = Alignment.Center,
    ) {
        Text(text = "This is the first screen.")}}// Second screen content
@Composable
fun OtherScreen(a) {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Red),
        contentAlignment = Alignment.Center
    ) {
        Text(text = "Here's another screen.")}}Copy the code

In the code, the layout is determined and switched directly with a Boolean value of first, without any animation, and looks very stiff.

Now to modify the LayoutSwitchAnimation() function above, we use Crossfade() to include the code to switch the layout as follows:

@Composable
fun LayoutSwitchAnimation(a) {
    var first by remember { mutableStateOf(true) }
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp) ) { Button(onClick = { first = ! first }, modifier = Modifier.padding(bottom =10.dp)) {
            Text(text = "Switch")}// Use Crossfade to add toggle animations
        Crossfade(
            targetState = first, // The status value to determine which screen to switch to
            animationSpec = tween(durationMillis = 2000) / / animation
        ) { first ->
            if (first) {
                FirstScreen()
            } else {
                OtherScreen()
            }
        }
    }
}
Copy the code

After adding the animation:

We just use the Crossfade to include the code for switching screens, and we achieve the switching animation, which is much more than the native XML layout. The Crossfade API is as follows:

@Composable
fun <T> Crossfade(
    targetState: T./ / state values
    modifier: Modifier = Modifier,
    animationSpec: FiniteAnimationSpec<Float> = tween()./ / animation
    content: @Composable (T) -> Unit
)
Copy the code

Only two of these parameters need special attention: targetState is the status value that we use to decide which layout to use; The animationSpec is the animation value that indicates what animation to use for the switch. The default is tween(), which is a gradient animation, as follows:

@Stable
fun <T> tween(
    durationMillis: Int = DefaultDurationMillis, // Animation length, default 300ms
    delayMillis: Int = 0.// Delay time
    easing: Easing = FastOutSlowInEasing // Animation effect, default fast out slow in
): TweenSpec<T> = TweenSpec(durationMillis, delayMillis, easing)
Copy the code

The code is very simple, just look at the comments, and easing is the key point. We don’t have to write the effects ourselves, because Compose already provides us with a number of default effects, which can be found in the file easing. Kt.

Value of the animation

The value animation adds a transition effect to the value change. Let’s say we want to click on a Button and change its background color. It can be written like this:

@Composable
fun ValueChangeAnimation(a) {
    var first by remember { mutableStateOf(true)}val bgColor = if (first) Color.Green else Color.Red // Switch between green and red
    Text(
        text = "Switch",
        Modifier
            .padding(32.dp) // Margin, equivalent to margin
            .background(bgColor) / / the background color.clickable { first = ! first }// Click to reverse the value of first
            .padding(8.dp) // Inner margin, equivalent to padding)}Copy the code

The effect is as follows:

Now to add the animation, we change the code to change the color to:

val bgColor by animateColorAsState(
    targetValue = if (first) Color.Green else Color.Red,
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioLowBouncy,
        stiffness = Spring.StiffnessVeryLow
    ),
)
Copy the code

The effect is as follows:

As you can see, the color switch now has a gradient effect. AnimateColorAsState is the API for adding value animations as follows:

fun animateColorAsState(
    targetValue: Color.// Target color value
    animationSpec: AnimationSpec<Color> = colorDefaultSpring, / / animation
    finishedListener: ((Color) - >Unit)? = null // The animation completes the callback
)
Copy the code

The focus is on animationSpec, which specifies animation effects. The default is spring in layout size animation.