Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I draw below canvas as segmented like 0.7 of path in jetpack compose?

I want to animate stroke path but I couldn't find a way to animate as segmented. How can I draw below canvas as segmented like 0.7 of path from starting point in jetpack compose?

  Canvas(modifier = Modifier.size(150.dp)) {
    val path = Path()
    val width: Float = size.width
    val height: Float = size.height
    path.apply {
        moveTo(width / 2, height / 5)
        cubicTo(
            5 * width / 14, 0f,
            0f, height / 15,
            width / 28, 2 * height / 5
        )
        cubicTo(
            width / 14, 2 * height / 3,
            3 * width / 7, 5 * height / 6,
            width / 2, height
        )
        cubicTo(
            4 * width / 7, 5 * height / 6,
            13 * width / 14, 2 * height / 3,
            27 * width / 28, 2 * height / 5
        )
        cubicTo(
            width, height / 15,
            9 * width / 14, 0f,
            width / 2, height / 5
        )
    }
    drawPath(color = Color.Red, path = path, style = Stroke(8f))
}
like image 467
Abdullah Avatar asked Dec 06 '25 07:12

Abdullah


1 Answers

You can do it with val pathMeasure by remember { mutableStateOf(PathMeasure()) } and setting start and end distances with

pathWithProgress.reset()

pathMeasure.setPath(path, forceClosed = false)
pathMeasure.getSegment(
    startDistance = 0f,
    stopDistance = pathMeasure.length * progress,
    pathWithProgress,
    startWithMoveTo = true
)

The snippet below starts from 0.7f of Path and based on progress, i used a Slider for demonstration but it can easily be aniamted, it can be drawn to full path length.

Result

enter image description here

Code

@Preview
@Composable
private fun Test() {

    var progress1 by remember {
        mutableStateOf(0.7f)
    }

    val transition: InfiniteTransition = rememberInfiniteTransition(label = "heart animation")

    // Infinite phase animation for PathEffect
    val progress2 by transition.animateFloat(
        initialValue = 0f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(
            animation = tween(
                durationMillis = 1500,
                easing = FastOutSlowInEasing
            ),
        ), label = "heart animation"
    )


    // This is the progress path which wis changed using path measure
    val pathWithProgress by remember {
        mutableStateOf(Path())
    }

    // using path
    val pathMeasure by remember { mutableStateOf(PathMeasure()) }

    val path = remember {
        Path()
    }

    Column(
        Modifier
            .fillMaxSize()
            .padding(20.dp)
    ) {

        Text(text = "Progress1: $progress1")
        Slider(
            value = progress1,
            onValueChange = { progress1 = it },
            valueRange = 0.7f..1f
        )

        Canvas(modifier = Modifier.size(150.dp)) {
            val width: Float = size.width
            val height: Float = size.height

            if (path.isEmpty) {
                path.apply {
                    moveTo(width / 2, height / 5)
                    cubicTo(
                        5 * width / 14, 0f,
                        0f, height / 15,
                        width / 28, 2 * height / 5
                    )
                    cubicTo(
                        width / 14, 2 * height / 3,
                        3 * width / 7, 5 * height / 6,
                        width / 2, height
                    )
                    cubicTo(
                        4 * width / 7, 5 * height / 6,
                        13 * width / 14, 2 * height / 3,
                        27 * width / 28, 2 * height / 5
                    )
                    cubicTo(
                        width, height / 15,
                        9 * width / 14, 0f,
                        width / 2, height / 5
                    )
                }
            }

            pathWithProgress.reset()

            pathMeasure.setPath(path, forceClosed = false)
            pathMeasure.getSegment(
                startDistance = 0f,
                stopDistance = pathMeasure.length * progress1,
                pathWithProgress,
                startWithMoveTo = true
            )

            drawPath(color = Color.Red, path = pathWithProgress, style = Stroke(8f))

            pathWithProgress.reset()

            pathMeasure.setPath(path, forceClosed = false)
            pathMeasure.getSegment(
                startDistance = 0f,
                stopDistance = pathMeasure.length * progress2,
                pathWithProgress,
                startWithMoveTo = true
            )

            translate(left = 500f) {
                drawPath(color = Color.Blue, path = pathWithProgress, style = Stroke(8f))
            }

        }
    }
}
like image 50
Thracian Avatar answered Dec 09 '25 04:12

Thracian