Search code examples
androidkotlinandroid-jetpack-composeandroid-jetpack-compose-canvasjetpack-compose-animation

Flow pulse animation in jetpack compose


I want to build flow pulse animation. I build something similar to that but that one is not accurate what I want for my requirement. I want something like this library.

@Composable
fun PulseView() {
    val infiniteTransition = rememberInfiniteTransition()
    val smallCircleSizeInPx = LocalDensity.current.run {
        6.dp.toPx()
    }
    val mediumCircleSize by infiniteTransition.animateValue(
        initialValue = 13.dp,
        targetValue = 11.dp,
        Dp.VectorConverter,
        animationSpec = infiniteRepeatable(
            animation = tween(500),
            repeatMode = RepeatMode.Reverse
        )
    )
    val largeCircleSize by infiniteTransition.animateValue(
        initialValue = 18.dp,
        targetValue = 16.dp,
        Dp.VectorConverter,
        animationSpec = infiniteRepeatable(
            animation = tween(500),
            repeatMode = RepeatMode.Reverse
        )
    )
    val middleCircleSizeInPx = LocalDensity.current.run { mediumCircleSize.toPx() }
    val largeCircleSizeInPx = LocalDensity.current.run { largeCircleSize.toPx() }
    val strokeWidth = LocalDensity.current.run { 2.dp.toPx() }
    val brush = Brush.linearGradient(listOf(NeutralsCloudy, NeutralsCloudy))
    PulseViewContent(
        smallCircleSizeInPx,
        middleCircleSizeInPx,
        largeCircleSizeInPx,
        strokeWidth,
        brush,
    )
}

PulseViewContent

@Composable
fun PulseViewContent(smallCircleSizeInPx: Float, middleCircleSizeInPx: Float, largeCircleSizeInPx: Float, strokeWidth: Float, brush: Brush) {
    
    Canvas(modifier = Modifier.fillMaxHeight()) {
        drawIntoCanvas {
            drawLine(
                brush = brush,
                start = Offset(x = size.width / 2, y = 0F),
                end = Offset(x = size.width / 2, y = size.height / 2),
                strokeWidth = strokeWidth,
            )
            drawLine(
                brush = brush,
                start = Offset(x = size.width / 2, y = size.height / 2),
                end = Offset(x = size.width / 2, y = size.height),
                strokeWidth = strokeWidth,
            )
            drawCircle(
                color = SeaGreen.copy(alpha = 0.32f),
                radius = largeCircleSizeInPx,
                center = Offset(size.width, size.height / 2)
            )
            drawCircle(
                color = SeaGreen.copy(alpha = 0.32f),
                radius = middleCircleSizeInPx,
                center = Offset(size.width, size.height / 2)
            )
            drawCircle(
                color = SeaGreen,
                radius = smallCircleSizeInPx,
                center = Offset(size.width, size.height / 2)
            )
        }
    }
}

Actual Output

You can find in the Youtube.

Expected Output

You can find in the Youtube.


Solution

  • I solve this problem with the help of this solution

    @Preview(showBackground = true)
    @Composable
    fun PulseAnimation() {
        Box(
            contentAlignment = Alignment.Center,
            modifier = Modifier.fillMaxSize()
        ) {
            val transition = rememberInfiniteTransition()
            val duration = 3_000
            val count = 3
            val progress = List(3) {
                transition.animateFloat(
                    initialValue = 0f,
                    targetValue = 1f,
                    animationSpec = InfiniteRepeatableSpec(
                        animation = tween(durationMillis = duration, easing = LinearEasing),
                        initialStartOffset = StartOffset(it * duration / count),
                        repeatMode = RepeatMode.Restart,
                    )
                )
            }
            Canvas(modifier = Modifier.fillMaxHeight()) {
                val radius = 40.dp.toPx() / 2f
                drawLine(
                    color = Color.Black,
                    start = Offset(x = size.width / 2, y = 0F),
                    end = Offset(x = size.width / 2, y = size.height / 2),
                    strokeWidth = 2.dp.toPx(),
                )
                progress.forEach {
                    drawCircle(
                        color = Color.Magenta.copy(alpha = 1f - it.value),
                        radius = radius * it.value,
                        center = Offset(size.width, size.height / 2)
                    )
                }
            }
        }
    }
    

    If anyone know better solution. Please free to update or provide solution. Thanks