Search code examples
androidandroid-jetpack-composeandroid-jetpack-compose-canvas

Confusing behaviour by the 'rotate' transformation of Compose Canvas


I have a simple Composable Canvas that draws a shape multiple times in different positions, and I wish to apply rotation to each iteration of the shape as per a particular algorithm. However, I am unable to fully control the positioning of the shapes in the canvas since the rotate transformation seems to apply a translation trasformation of its own. Here's what I have

    @Preview
    @Composable
    fun CanvasCollosum() {
        val painter = rememberVectorPainter(image = ImageVector.vectorResource(id = R.drawable.tick))
        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center){
            Canvas(modifier = Modifier.height(200.dp).aspectRatio(1f)) {
                with(painter) {
                    repeat(12) {
                        (it * 30f).let { angle ->
                            translate(
                                top = 0f, // Some translation is still applied by 'rotate'
                                left = 0f
                            ) {
                                rotate(angle) {
                                    draw(
                                        Size(100f, 100f),
                                        colorFilter = ColorFilter.tint(Color.White)
                                    )
                                }
                            }
                        }
                    }
                }
            }
        }
    }

Hence, for some reason, the positionings of all of the shapes here (12 in total) assume the shape of a circle, which is not at all what I expected. I can assume it is something related to the 'pivot' of the rotation. However, it is set, by default, as the center of the Canvas, which seems fairly appropriate. The shape is being rendered far away from the pivot which seems to be causing the translation effect to occur as a side effect, so the question is -- How do I render all my shapes with a fixed rotation, and position them with specific (x, y) cords, given that I have an algorithm to position them relative to the center of the Canvas?

For reference, here's some outputs, displaying a single shape, with the only varying property being the rotation angle

0f Degrees

0 Degrees

90f Degrees

enter image description here

180f Degrees

enter image description here

270f Degrees

enter image description here

360f is the same as 0f

This does not seem like something that should be happening here, but it is.

The Square is the Box Composable and the little white thing is the shape in concern.


Solution

  • It is not applying translation, but is rotating around a pivot, which is set to the center of the Box.

    rotate has a parameter named pivot that will solve your problem:

    @Composable
    fun Rotation() {
        val painter = rememberVectorPainter(image = Icons.Default.ThumbUp)
        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
            Canvas(
                modifier = Modifier
                    .height(200.dp)
                    .background(color = Color.Red)
                    .aspectRatio(1f)
            ) {
                with(painter) {
                    repeat(4) {
                        rotate(
                            degrees = (it * 90f),
                            pivot = Offset(
                                painter.intrinsicSize.width * 2,
                                painter.intrinsicSize.height * 2
                            )
                        ) {
                            draw(
                                Size(100f, 100f),
                                colorFilter = ColorFilter.tint(Color.White)
                            )
                        }
                    }
                }
            }
        }
    }
    

    The code above produces this result:

    In order to change the point, around which the rotation is done, simple change the coordinates in pivot parameter.