[TIL] ๐ŸŒผ24/04/25๐ŸŒผ#infiniteTransition #updateTransition

0

TIL

๋ชฉ๋ก ๋ณด๊ธฐ
87/104
post-thumbnail
post-custom-banner

[TIL] ๐ŸŒผ24/04/25๐ŸŒผ#infiniteTransition #updateTransition

๐Ÿ“Œ์ฐธ๊ณ ์ž๋ฃŒ

  • ์˜ค๋Š˜์€ BMI ๊ณ„์‚ฐ๊ธฐ ์•ฑ์— ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ ์šฉํ•ด๋ณด์•˜๋‹ค.

  • Jetpack Compose๋Š” ๋‹ค์–‘ํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ API๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
    -> ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ•˜๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ API ์„ ํƒํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

Infinite Transition

๐Ÿ“Œ์ฐธ๊ณ ์ž๋ฃŒ

  • ๋ฌดํ•œ ๋ฐ˜๋ณต๋˜๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ตฌํ˜„ํ•  ๋•Œ rememberInfiniteTransition ์‚ฌ์šฉ
  • ๋ฌดํ•œ ๋ฐ˜๋ณต ๋ชจ๋“œ: RepeatMode.Restart ๋˜๋Š” RepeatMode.Reverse
@Composable
fun PulsingImage(
    @DrawableRes imageId: Int
) {
    val infiniteTransition = rememberInfiniteTransition(label = "pulse_image")
    val scale = infiniteTransition.animateFloat(
        label = "pulse_image",
        initialValue = 0.3F,
        targetValue = 0.6F,
        animationSpec = infiniteRepeatable(
            animation = tween(1000),
            repeatMode = RepeatMode.Reverse
        )
    )

    Image(
        painter = painterResource(id = imageId),
        contentDescription = "",
        modifier = Modifier
            .graphicsLayer(
                scaleX = scale.value,
                scaleY = scale.value
            )
            .clipToBounds()
    )
}

Update Transition

์ฐธ๊ณ ์ž๋ฃŒ

  • composition์— ๋“ค์–ด๊ฐ€์ž๋งˆ์ž ์‹œ์ž‘๋˜๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ตฌํ˜„ํ•  ๋•Œ updateTransition๊ณผ MutableTransitionState ์‚ฌ์šฉ
  • MutableTransitionState
    • ๋‘ ๊ฐœ์˜ ํ•„๋“œ currentState, targetState๋ฅผ ๊ฐ€์ง
    • currentState: ์ดˆ๊ธฐ๊ฐ’ ์„ค์ • ๊ฐ€๋Šฅ, Transition์„ ํ†ตํ•ด์„œ๋งŒ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ
    • targetState: ์ดˆ๊ธฐ๊ฐ’ ์„ค์ • ๊ฐ€๋Šฅ, ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ
  • BMI ๊ฒฐ๊ณผ ํŒจ๋„ ํ™”์‚ดํ‘œ ํšŒ์ „ ์• ๋‹ˆ๋ฉ”์ด์…˜
    • TransformOrigin์„ ์‚ฌ์šฉํ•ด ์ด๋ฏธ์ง€ ํšŒ์ „์ถ• ์„ค์ •
enum class ResultPanelState {
    BEFORE,
    AFTER
}

@Composable
fun ResultPanel(
    modifier: Modifier = Modifier,
    targetDegree: Float
) {
    val resultPanelState = remember { MutableTransitionState(ResultPanelState.BEFORE) }
    resultPanelState.targetState = ResultPanelState.AFTER

    val panelTransition = updateTransition(
        transitionState = resultPanelState, label = "rotate_panel"
    )
    val panelDegree = panelTransition.animateFloat(
        label = "rotate_panel",
        transitionSpec = { tween(1500) },
        targetValueByState = { state ->
            when (state) {
                ResultPanelState.BEFORE -> -225F
                ResultPanelState.AFTER -> targetDegree
            }
        }
    )

    Box(
        modifier = modifier
            .width(150.dp)
            .height(150.dp)
    ) {
        Image(
            painter = painterResource(id = R.drawable.result_panel),
            contentDescription = "",
            modifier = modifier.align(Alignment.BottomCenter)
        )
        Image(
            painter = painterResource(id = R.drawable.result_panel_arrow),
            contentDescription = "",
            modifier = modifier
                .align(Alignment.BottomCenter)
                .padding(start = 32.dp)
                .scale(0.5F)
                .graphicsLayer(
                    rotationZ = panelDegree.value,
                    transformOrigin = TransformOrigin(
                        pivotFractionX = 0.26F,
                        pivotFractionY = 0.44F
                    )
                )
        )
    }
}
  • BMI ๊ฒฐ๊ณผ ํ…์ŠคํŠธ ํšŒ์ „ ์• ๋‹ˆ๋ฉ”์ด์…˜
    • ํ…์ŠคํŠธ ํด๋ฆญ ์‹œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์žฌ์‹คํ–‰ ๋˜๋„๋ก ๊ตฌํ˜„
    • recomposition ์‹œ targetState๊ฐ€ ์ƒˆ๋กœ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š๋„๋ก key๊ฐ€ Unit์ธ LaunchedEffect ์‚ฌ์šฉ
enum class RotationState {
    BEFORE,
    AFTER
}

@Composable
fun RotatingText(
    text: String,
    textColor: Int
) {
    val textRotationState = remember { MutableTransitionState(RotationState.BEFORE) }
    LaunchedEffect(Unit) {
        textRotationState.targetState = RotationState.AFTER
    }

    val rotationTransition = updateTransition(
        transitionState = textRotationState,
        label = "rotate_text"
    )
    val rotation = rotationTransition.animateFloat(
        label = "rotate_text",
        transitionSpec = {
            tween(3000)
        },
        targetValueByState = { state ->
            when (state) {
                RotationState.BEFORE -> 360F
                RotationState.AFTER -> 0F
            }
        }
    )

    Text(
        text = text,
        color = Color(textColor),
        modifier = Modifier
            .graphicsLayer(
                rotationX = rotation.value,
                rotationY = rotation.value
            )
            .clickable {
                with(textRotationState) {
                    targetState =
                        if (currentState == RotationState.AFTER) RotationState.BEFORE
                        else RotationState.AFTER
                }
            }
    )
}

post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€