This afternoon I've been trying to get an animatio...
# compose
n
This afternoon I've been trying to get an animation to run as follows: when some state, call it
isVisible
is false, animate the alpha of a component infinitely between zero and one. If
isVisible
is true, animate the current alpha up to 1.0f. Code in thread.
What I see is appropriate animation, cycling when isVisible is false. But then immediately going to 1.0f alpha. Like the update transition just immediately transitions to 1.0f.
d
I'd recommend using
Animatable
for this use case:
Copy code
val alpha = remember { Animatable(0f) }
LaunchedEffect(isVisible) { // This restarts the job when isVisible changes
    alpha.animateTo(1f)
    if (!isVisible) { // Once at 1f, check whether repeating is needed.
        alpha.animateTo(0f, animationSpec = infiniteRepeatable(...))
    }
}
☝️ 1
☝🏼 1
If you switch between
infiniteTransition
and
transition
, the animation value won't be preserved during the
isVisible
change. Hence there'll likely be a blink.
n
Okay, that's really nice. Will try that. I did manage to get this mostly working, but seemed like a bit of a hack:
Copy code
var started: Boolean by remember { mutableStateOf(true) }

    // Launch a state change to make fake data visible
    var isVisible by remember { mutableStateOf(false) }
    LaunchedEffect(viewModel) {
        started = false
        delay(5000)
        isVisible = !isVisible
    }

    val alpha: Float by animateFloatAsState(if (isVisible || started) 1.0f else 0.0f, animationSpec =
    if (isVisible) tween(1000) else infiniteRepeatable(
        animation = tween(1500),
        repeatMode = RepeatMode.Reverse
    ))
d
This
val alpha: Float by animateFloatAsState(if (isVisible || started) 1.0f else 0.0f...))
would work if you only flip
isVisible
when it animates to 1f, otherwise it'd be infinitely animating between 0f and whatever the value is at the point of interruption. 🙂
n
I just tried your code, Doris, and it works really well. I ended up with the following:
Copy code
val alpha = remember { Animatable(0f) }
    LaunchedEffect(isVisible) { // This restarts the job when isVisible changes
        alpha.animateTo(1f, animationSpec = tween(1000))
        if (!isVisible) { // Once at 1f, check whether repeating is needed.
            alpha.animateTo(
                0f, animationSpec = infiniteRepeatable(
                    animation = tween(1500),
                    repeatMode = RepeatMode.Reverse
                )
            )
        }
    }
Just to show what I'm working on, here's what the above animation ends up looking like. This is just a prototype, not a real product.
@Doris Liu, thanks very much for the assistance. 🙂
d
Glad to help. The prototype looks pretty neat! 👏😀
n
Not sure what happens at the 7s range of that video... chalking it up to beta jank right now. This is just me fooling around, the designer I work with hasn't told me to do these animations. Just taking the time to learn some of these APIs.
The rest of the animations are much smoother when I remove
Copy code
, animationSpec = tween(1000)
from the above code. I'll just leave it that way.
d
At ~7s is probably when
isVisible
was changed. The default spring animation will account for the velocity of the alpha change and make it smooth, whereas a
tween
would animate the current value to the target in the same amount of time no matter what the delta is. Alternatively, you could have the animation run between 0 and 1, and only check whether there should be another iteration after it arrives at 1f (didn't test this code but it should work):
Copy code
val alpha = remember { Animatable(1f) }
    LaunchedEffect(!isVisible || alpha.isRunning) { // This restarts the job when isVisible = true & alpha animation is finished
        while (!isVisible) {
            alpha.animateTo(0f, animationSpec = tween(1500))
            alpha.animateTo(1f, animationSpec = tween(1500))
        }
    }
n
My comment about ~7s was in relation to the other animations in my composition. I have a set of
AnimatedVisibility
composables scattered around my code, and if this alpha animation is running a tween, those animations stutter and skip frames. Removing the
tween
and having just the default spring animation in place seems to improve the animation of visibility for other elements. I'm not worried about this at the moment, I suspect the performance will be improved in future beta releases.