Is there a sample or some doc about playing transi...
# compose
y
Is there a sample or some doc about playing transitions sequentially?
Copy code
var animationIndex by remember { mutableStateOf(0) }
val definition = remember(animationIndex) { buildTransition(animationIndex = animationIndex) }
val state = transition(
    definition = definition,
    initState = AnimationState.START,
    toState = AnimationState.END,
    onStateChangeFinished = {
        if (animationIndex < animationCount) {
            animationIndex++
        }
    }
)
This works, but feels weird, and has some drawbacks: • Doesn't work well with @Preview animation inspector •
animationIndex
can't be used in the code depending on the animation, because when one animation is completed, there's a brief moment where
animationIndex
was incremented but
state
is still on the
AnimationState.END
because the next one didn't start. So I have to add the index as a key inside the state to fix this. It works fine, but error prone
d
There's an interruption handling mode: uninterruptible. If you set that on a transition from A->B, and change toState to C during the transition, you'll get a complete run from A to B then to C. Here's an example.
If this doesn't work for your use case, I'd love to know more about your specific use case. Perhaps we could add a concept like a waypoint. At the moment, waypoints could be achieved via keyframes, with some effort. 🙂
y
Thanks for the sample, I'm not sure it would simplify the use case I have. I thought of using keyframes, however correct me if I'm worng, but you can't have an interpolator set between each key frame right? I'm reproducing the following animation:

https://github.com/badoualy/kanji-strokeview/raw/master/ART/preview.gif

Each stroke is a
Path
that I animate (with a
DashPathEffect
). Each stroke is considered as an independant animation => the interpolator/duration settings should be different for each stroke. I feel like it's a mix case of keyframe and tween. In this sense, the waypoint concept would be nice, to be able to iterate through states easily.
I got it to work using
nextState
that I found in your sample, thanks! But I think a wrapper API to avoid having to do that manually would be nice :)
Copy code
private sealed class AnimatedStrokeState {
    data class START(val strokeIndex: Int) : AnimatedStrokeState()
    data class END(val strokeIndex: Int) : AnimatedStrokeState()
}

private fun buildTransition(
    pathMeasureList: List<PathMeasure>
): TransitionDefinition<AnimatedStrokeState> {
    return transitionDefinition {
        pathMeasureList.forEachIndexed { i, pathMeasure ->
            val startState = AnimatedStrokeState.START(i)
            state(startState) {
                this[StrokeIndex] = i
                this[StrokeProgress] = 0f
            }
            val endState = AnimatedStrokeState.END(i)
            state(endState) {
                this[StrokeIndex] = i
                this[StrokeProgress] = 1f
            }

            val isLastPath = i == pathMeasureList.lastIndex
            transition(startState to endState) {
                StrokeProgress using tween(
                    delayMillis = if (i == 0) 250 else 0,
                    durationMillis = (pathMeasure.length * 10L).toInt(), // Panoramix' formula
                    easing = FastOutSlowInEasing
                )
                if (!isLastPath) {
                    // Chain
                    nextState = AnimatedStrokeState.START(i + 1)
                }
            }

            if (!isLastPath) {
                snapTransition(
                    endState to AnimatedStrokeState.START(i + 1),
                    nextState = AnimatedStrokeState.END(i + 1)
                )
            }
        }
    }
}
It does feel weird though having to declare the transition as:
Copy code
val state = transition(
        definition = definition,
        initState = AnimatedStrokeState.START(0),
        toState = AnimatedStrokeState.END(0)
    )
Instead of something like:
Copy code
val state = transition(
    definition = definition,
    initState = AnimatedStrokeState.START(0),
    toState = AnimatedStrokeState.END(strokes.lastIndex)
)
which we could have with waypoint/intermediary steps Also this approach doesn't make it possible to run the full animation on android studio's animation inspector. We can only inspect from one state to another: "nextState" is not used
d
You could have different easing for different intervals in keyframes: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/KeyframesSpec
I agree with you that waypoints feel more natural here than going to the first state and auto transition to the next state. We'll likely replace the next state concept with way point, since next state creates an inconsistency with the input state (through
transition
) and the actual state that the animation ends on.
Cool animation BTW. I'm glad you are using the studio animation inspector. More features are coming, so stay tuned. 🙂
y
Very nice to be able to configure the easing on each keyframe, thanks for pointing that out! Looking forward to waypoints and new features then. The inspector is awesome, just very slow to init, but I expect it'll get better by the time compose is production ready. Thanks for you help :)