Search code examples
androidkotlincanvasandroid-jetpack-composeandroid-jetpack-compose-modifier

Weird behaviour from Interactive Line in Cavnas


I created an interactive line, but that might be irrelevant. Even if there was no interaction, this renders unexpected results:-

@Composable
fun PowerClock() {
    var dynamicAngle by remember { mutableStateOf(90f.toRadians()) }
    val angle by animateFloatAsState(targetValue = dynamicAngle)
    Canvas(
        modifier = Modifier
            .fillMaxHeight()
            .fillMaxWidth()
            .pointerInput(Unit) { //Irrelevent, the results go wrong even without invoking this at all
                coroutineScope {

                    while (true) {
//                        val touchDownPointerId = awaitPointerEventScope { awaitFirstDown().id }
                        detectDragGestures { _, dragAmount ->
                            dynamicAngle += atan(dragAmount.x / dragAmount.y)
                        }
                    }
                }
            }
    ) {
        val length = 500
        val path = Path().apply {
            moveTo(size.width / 2, size.height / 2)
            relativeLineTo(length * cos(angle), length * sin(angle))
        }

        drawPath(path, Color.Blue, style = Stroke(10f))
    }
}

Here's a bit of a preview,

An intriguing behaviour portrayed by Cavnas is that looking at my implementation, the angle should change based on both the x and y change, right? But in actuality, y is out ignored. I have tested this.

Is this a bug in Cavnas or am I implementing something wrong?


Solution

  • I've followed this answer and adopted code to Compose:

    var touchPosition by remember { mutableStateOf(Offset.Zero) }
    Canvas(
        modifier = Modifier
            .fillMaxHeight()
            .fillMaxWidth()
            .pointerInput(Unit) { //Irrelevent, the results go wrong even without invoking this at all
                while (true) {
                    detectDragGestures { change, _ ->
                        touchPosition = change.position
                    }
                }
            }
    ) {
        val rect = Rect(Offset.Zero, size)
        val length = 500
        val path = Path().apply {
            moveTo(rect.center.x, rect.center.y)
            val angle = (touchPosition - rect.center).let { atan2(it.y, it.x) }
            relativeLineTo(length * cos(angle), length * sin(angle))
        }
    
        drawPath(path, Color.Blue, style = Stroke(10f))
    }
    

    Result: