[Android] 낙엽 떨어지는 효과

uuranus·2024년 8월 16일
0
post-thumbnail

기존 애니메이션 분석

포슬립 화면

이 장면에서 빛이 내려오는 효과를 구현했으니 이제 나뭇잎도 떨어지는 효과를 만들어 볼 것이다.

분석을 해보자면
1. 오른쪽에서 대각선으로 모두 일정한 방향으로 떨어짐
2. 떨어지면서 회전함

이렇게 2가지 종류의 애니메이션이 있다.

초기 위치 설정

var things by remember {
    mutableStateOf(
        List(10) {
            val x = Random.nextInt(screenWidthPx.toInt()).toFloat()
            val y = Random.nextInt(screenHeightPx.toInt() / 2).toFloat()

            val offset = if (Random.nextInt(2) == 1) {
                Offset(x, -y)
            } else {
                Offset(screenWidthPx + x, y)
            }

            val imageSize = Random.nextInt(10, 40).dp
            Leaf(offset, imageSize, 0f)
        }
    )
}

빛이 내리는 효과랑 비슷하게 초기값을 랜덤으로 설정해주는데 위쪽과 오른쪽 바깥쪽에서 생성되도록 해주었다.

떨어지는 효과

LaunchedEffect(Unit) {
    while (true) {
        things = things.map { thing ->
            val newX = thing.offset.x - 20f
            val newY = thing.offset.y + 20f

            val offset = if (newX <= 0 || newY >= screenHeightPx) {
                val x = Random.nextInt(screenWidthPx.toInt()).toFloat()
                val y = Random.nextInt(screenHeightPx.toInt() / 2).toFloat()

                if (Random.nextInt(2) == 1) {
                    Offset(x, -y)
                } else {
                    Offset(screenWidthPx + x, y)
                }
            } else {
                Offset(newX, newY)
            }

            val rotate = (thing.rotate + 2) % 360
            thing.copy(offset = offset, rotate = rotate)
        }

        delay(100L)
    }
}

20f만큼 왼쪽 아래로 모두가 일정하게 이동하도록 하였다.
화면 밖으로 나가면 다시 초기값을 생성한다.

회전시키기

위 코드를 보면

val rotate = (thing.rotate + 2) % 360

이 있다. 초기 rotate값을 기준으로 2도씩 계속 돌아가도록 설정해주었다.

최종 결과

전체 코드

@Composable
fun WindBlownDiagonalEffect(
    resourceId: Int,
) {

    val configuration = LocalConfiguration.current
    val density = LocalDensity.current

    val screenWidthPx = with(density) {
        configuration.screenWidthDp * this.density
    }

    val screenHeightPx = with(density) {
        configuration.screenHeightDp * this.density
    }

    var things by remember {
        mutableStateOf(
            List(10) {
                val x = Random.nextInt(screenWidthPx.toInt()).toFloat()
                val y = Random.nextInt(screenHeightPx.toInt() / 2).toFloat()

                val offset = if (Random.nextInt(2) == 1) {
                    Offset(x, -y)
                } else {
                    Offset(screenWidthPx + x, y)
                }

                val imageSize = Random.nextInt(10, 40).dp
                Leaf(offset, imageSize, 0f)
            })
    }

    LaunchedEffect(Unit) {
        while (true) {
            things = things.map { thing ->
                val newX = thing.offset.x - 20f
                val newY = thing.offset.y + 20f

                val offset = if (newX <= 0 || newY >= screenHeightPx) {

                    val x = Random.nextInt(screenWidthPx.toInt()).toFloat()
                    val y = Random.nextInt(screenHeightPx.toInt() / 2).toFloat()

                    if (Random.nextInt(2) == 1) {
                        Offset(x, -y)
                    } else {
                        Offset(screenWidthPx + x, y)
                    }
                } else {
                    Offset(newX, newY)
                }

                val rotate = (thing.rotate + 2) % 360
                thing.copy(offset = offset, rotate = rotate)
            }

            delay(100L)
        }
    }

    val imageVector = ImageBitmap.imageResource(id = resourceId)

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Transparent)
    ) {

        for (thing in things) {

            val offsetX = with(density) {
                thing.offset.x.toDp()
            }
            val offsetY = with(density) {
                thing.offset.y.toDp()
            }

            Image(
                bitmap = imageVector,
                contentDescription = null,
                modifier = Modifier
                    .size(thing.size)
                    .offset(
                        x = offsetX,
                        y = offsetY
                    )
                    .rotate(thing.rotate),
                colorFilter = ColorFilter.tint(color = Color(0xFFAE8B4E))
            )
        }
    }
}

data class Leaf(
    val offset: Offset,
    val size: Dp,
    val rotate: Float,
)
profile
Frontend Developer

0개의 댓글