androidandroid-jetpack-composeandroid-animationjetpack-compose-animation

How to pause/resume a jetpack compose infinite transition animation


For my android app i'm using a custom modifier which draws a dashed border as detailed in this stackOverflow answer.

I now want to conditionally animate that dashed border: If the view is currently selected, then animate, else pause.

I managed to implement this behavior using an infinite transition. However, there's some "jumping" motion when toggling animate, which i'd like to avoid, e.g. by just pausing the infiniteTransition. But how do i do this?

fun Modifier.dashedBorder(
    color: Color,
    strokeWidth: Dp,
    strokeLength: Dp,
    animate: Boolean = false,
) = composed(
    factory = {
        val density = LocalDensity.current
        val strokeWidthPx = density.run { strokeWidth.toPx() }
        val strokeLengthPx = density.run { strokeLength.toPx() }

        val infiniteTransition = rememberInfiniteTransition()
        val offset by infiniteTransition.animateFloat(
            initialValue = 0f,
            targetValue = strokeLengthPx * 2,
            animationSpec = infiniteRepeatable(
                animation = tween(1000, easing = LinearEasing),
                repeatMode = RepeatMode.Restart
            )
        )

        this.then(
            Modifier.drawWithCache {
                onDrawBehind {
                    val stroke = Stroke(
                        width = strokeWidthPx,
                        pathEffect = PathEffect.dashPathEffect(
                            intervals = floatArrayOf(strokeLengthPx, strokeLengthPx),
                            phase = if (animate) offset else 0f, // <-- cause of jumping motion
                        )
                    )

                    drawRect(
                        color = color,
                        style = stroke,
                    )
                }
            }
        )
    }
)

enter image description here


Solution

  • Hope it will be helpful to you:

    enter image description here

       fun Modifier.dashedBorder(
            color: Color,
            strokeWidth: Dp,
            strokeLength: Dp,
            animate: Boolean = true,
        ) = composed(
            factory = {
                val density = LocalDensity.current
                val strokeWidthPx = density.run { strokeWidth.toPx() }
                val strokeLengthPx = density.run { strokeLength.toPx() }
                // store the last animate value be next animate start
                var lastAnimValue by remember { mutableStateOf(0f) }
                val anim = remember(animate) { Animatable(lastAnimValue) }
    
                LaunchedEffect(animate) {
                    if (animate) {
                        anim.animateTo(
                            // Important !!! Animate Target need add the lastAnim value, Simple math knowledge :)
                            (strokeLengthPx * 2 + lastAnimValue), animationSpec =
                            infiniteRepeatable(
                                animation = tween(1000, easing = LinearEasing),
                                repeatMode = RepeatMode.Restart,
                            )
                        ) {
                            lastAnimValue = value // store the anim value
                        }
                    }
                }
    
                this.then(
                    Modifier.drawWithCache {
                        onDrawBehind {
                            val stroke = Stroke(
                                width = strokeWidthPx,
                                pathEffect = PathEffect.dashPathEffect(
                                    intervals = floatArrayOf(strokeLengthPx, strokeLengthPx),
                                    phase = anim.value, // always use the anim
                                )
                            )
                            drawRect(
                                color = color,
                                style = stroke,
                            )
                        }
                    }
                )
            }
        )