1

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

m.reiter
  • 1,796
  • 2
  • 11
  • 31

1 Answers1

1

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,
                        )
                    }
                }
            )
        }
    )
LuoBo
  • 92
  • 7