Compose의 애니메이션에 대한 빠른 가이드

Compose에는 많은 기본 제공 애니메이션 메커니즘이 있으며 어느 것을 선택해야 할지 알 수 있습니다. 다음은 일반적인 애니메이션 사용 사례 목록입니다. 대상 사용 가능한 다양한 API 옵션의 전체 집합에 대한 자세한 정보 전체 Compose 애니메이션 문서를 참고하세요.

일반적인 구성 가능한 속성에 애니메이션 적용

Compose는 개발자가 자주 사용하는 여러 문제를 해결할 수 있는 편리한 API를 제공합니다. 살펴보겠습니다. 이 섹션에서는 일반 컴포저블의 속성입니다.

나타나거나 사라지는 애니메이션

자신을 표시하고 숨기는 녹색 컴포저블
그림 1. 열에서 항목이 표시되고 사라지는 경우에 애니메이션 적용
를 통해 개인정보처리방침을 정의할 수 있습니다.

AnimatedVisibility를 사용하여 컴포저블을 숨기거나 표시합니다. 내부 어린이 AnimatedVisibility님이 직접 Modifier.animateEnterExit()을(를) 사용할 수 있습니다. 또는 전환을 종료할 수 있습니다.

var visible by remember {
    mutableStateOf(true)
}
// Animated visibility will eventually remove the item from the composition once the animation has finished.
AnimatedVisibility(visible) {
    // your composable here
    // ...
}

AnimatedVisibility의 들어가기 및 나가기 매개변수를 사용하면 컴포저블이 표시되었다가 사라질 때 동작합니다. 전체 읽기 문서를 참조하세요.

컴포저블의 공개 상태를 애니메이션화하는 또 다른 옵션은 animateFloatAsState를 사용하여 시간 경과에 따른 알파 버전을 얻습니다.

var visible by remember {
    mutableStateOf(true)
}
val animatedAlpha by animateFloatAsState(
    targetValue = if (visible) 1.0f else 0f,
    label = "alpha"
)
Box(
    modifier = Modifier
        .size(200.dp)
        .graphicsLayer {
            alpha = animatedAlpha
        }
        .clip(RoundedCornerShape(8.dp))
        .background(colorGreen)
        .align(Alignment.TopCenter)
) {
}

하지만 알파를 변경하면 컴포저블이 유지되고 구성되고 계속해서 배치되는 공간을 차지합니다. 이 스크린 리더와 기타 접근성 메커니즘에서 화면에 표시된 항목을 찾습니다. 반면에 AnimatedVisibility는 결국 음악작품에서 항목을 선택합니다.

컴포저블의 알파에 애니메이션 적용
그림 2. 컴포저블의 알파에 애니메이션 적용
를 통해 개인정보처리방침을 정의할 수 있습니다.

배경 색상에 애니메이션 적용

시간이 지남에 따라 색상이 바뀌면서 색상이 서로 희미해지는 애니메이션으로 변경되는 컴포저블입니다.
그림 3. 컴포저블의 배경 색상에 애니메이션 적용
를 통해 개인정보처리방침을 정의할 수 있습니다.

val animatedColor by animateColorAsState(
    if (animateBackgroundColor) colorGreen else colorBlue,
    label = "color"
)
Column(
    modifier = Modifier.drawBehind {
        drawRect(animatedColor)
    }
) {
    // your composable here
}

이 옵션은 Modifier.background()를 사용하는 것보다 더 효율적입니다. Modifier.background()는 원샷 색상 설정에는 허용되지만 시간이 지남에 따라 색상을 애니메이션으로 표시하면 이전보다 더 많은 리컴포지션이 있습니다.

배경 색상을 무한으로 애니메이션으로 표시하려면 애니메이션 반복을 참조하세요. 섹션을 참조하세요.

컴포저블의 크기에 애니메이션 적용

크기 변경을 부드럽게 애니메이션하는 녹색 컴포저블
그림 4. 작은 크기와 큰 크기 사이에서 원활하게 애니메이션되는 컴포저블
를 통해 개인정보처리방침을 정의할 수 있습니다.

Compose를 사용하면 여러 가지 방법으로 컴포저블의 크기에 애니메이션을 적용할 수 있습니다. 사용 animateContentSize(): 컴포저블 크기 변경 간의 애니메이션

예를 들어 한 칸에서 한 쪽으로 펼칠 수 있는 텍스트가 포함된 상자가 있는 경우 Modifier.animateContentSize()를 사용하여 더 매끄럽게 만들 수 있습니다. 전환:

var expanded by remember { mutableStateOf(false) }
Box(
    modifier = Modifier
        .background(colorBlue)
        .animateContentSize()
        .height(if (expanded) 400.dp else 200.dp)
        .fillMaxWidth()
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            expanded = !expanded
        }

) {
}

AnimatedContentSizeTransform와 함께 사용하여 설명할 수도 있습니다. 크기 변경이 이루어지는 방식을 지정합니다.

컴포저블 위치에 애니메이션 적용

오른쪽 아래로 부드럽게 애니메이션되는 녹색 컴포저블
그림 5. 컴포저블 오프셋으로 이동
를 통해 개인정보처리방침을 정의할 수 있습니다.

컴포저블의 위치에 애니메이션을 적용하려면 Modifier.offset{ }를 다음과 함께 사용합니다. animateIntOffsetAsState()입니다.

var moved by remember { mutableStateOf(false) }
val pxToMove = with(LocalDensity.current) {
    100.dp.toPx().roundToInt()
}
val offset by animateIntOffsetAsState(
    targetValue = if (moved) {
        IntOffset(pxToMove, pxToMove)
    } else {
        IntOffset.Zero
    },
    label = "offset"
)

Box(
    modifier = Modifier
        .offset {
            offset
        }
        .background(colorBlue)
        .size(100.dp)
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            moved = !moved
        }
)

컴포저블이 다른 클래스 위에 또는 아래에 그려지지 않게 하려는 경우 컴포저블이 위치나 크기에 애니메이션을 적용할 때 Modifier.layout{ }를 사용합니다. 이 수정자는 크기 및 위치 변경사항을 상위 요소에 전파하여 다른 아이들도 있습니다.

예를 들어 Column와 다른 하위 요소 내에서 Box를 이동하는 경우 Box 이동 시 이동해야 하는 경우 다음과 같이 오프셋 정보를 포함합니다. Modifier.layout{ }:

var toggled by remember {
    mutableStateOf(false)
}
val interactionSource = remember {
    MutableInteractionSource()
}
Column(
    modifier = Modifier
        .padding(16.dp)
        .fillMaxSize()
        .clickable(indication = null, interactionSource = interactionSource) {
            toggled = !toggled
        }
) {
    val offsetTarget = if (toggled) {
        IntOffset(150, 150)
    } else {
        IntOffset.Zero
    }
    val offset = animateIntOffsetAsState(
        targetValue = offsetTarget, label = "offset"
    )
    Box(
        modifier = Modifier
            .size(100.dp)
            .background(colorBlue)
    )
    Box(
        modifier = Modifier
            .layout { measurable, constraints ->
                val offsetValue = if (isLookingAhead) offsetTarget else offset.value
                val placeable = measurable.measure(constraints)
                layout(placeable.width + offsetValue.x, placeable.height + offsetValue.y) {
                    placeable.placeRelative(offsetValue)
                }
            }
            .size(100.dp)
            .background(colorGreen)
    )
    Box(
        modifier = Modifier
            .size(100.dp)
            .background(colorBlue)
    )
}

두 번째 상자는 X, Y 위치에 애니메이션으로 표시되고 세 번째 상자도 Y만큼 움직이면서 반응합니다.
그림 6. Modifier.layout{ }로 애니메이션 적용

컴포저블의 패딩 애니메이션

클릭 시 초록색 컴포저블이 점점 작아지고 패딩이 애니메이션 처리됨
그림 7. 패딩 애니메이션이 적용된 컴포저블
를 통해 개인정보처리방침을 정의할 수 있습니다.

컴포저블의 패딩에 애니메이션을 적용하려면 animateDpAsState를 다음과 함께 사용합니다. Modifier.padding():

var toggled by remember {
    mutableStateOf(false)
}
val animatedPadding by animateDpAsState(
    if (toggled) {
        0.dp
    } else {
        20.dp
    },
    label = "padding"
)
Box(
    modifier = Modifier
        .aspectRatio(1f)
        .fillMaxSize()
        .padding(animatedPadding)
        .background(Color(0xff53D9A1))
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            toggled = !toggled
        }
)

컴포저블의 엘리베이션 애니메이션

<ph type="x-smartling-placeholder">
그림 8. 클릭 시 컴포저블의 엘리베이션 애니메이션

컴포저블의 고도에 애니메이션을 적용하려면 animateDpAsState를 다음과 함께 사용합니다. Modifier.graphicsLayer{ }입니다. 일회성 고도 변경의 경우 다음을 사용합니다. Modifier.shadow() 그림자를 애니메이션 처리하는 경우 Modifier.graphicsLayer{ } 수정자는 성능이 더 뛰어난 옵션입니다.

val mutableInteractionSource = remember {
    MutableInteractionSource()
}
val pressed = mutableInteractionSource.collectIsPressedAsState()
val elevation = animateDpAsState(
    targetValue = if (pressed.value) {
        32.dp
    } else {
        8.dp
    },
    label = "elevation"
)
Box(
    modifier = Modifier
        .size(100.dp)
        .align(Alignment.Center)
        .graphicsLayer {
            this.shadowElevation = elevation.value.toPx()
        }
        .clickable(interactionSource = mutableInteractionSource, indication = null) {
        }
        .background(colorGreen)
) {
}

또는 Card 컴포저블을 사용하고 고도 속성을 다음과 같이 설정합니다. 값이 다를 수 있습니다

텍스트 크기, 변환 또는 회전 애니메이션

다음과 같은 텍스트 컴포저블
그림 9. 두 가지 크기 간에 부드럽게 텍스트 애니메이션 처리
를 통해 개인정보처리방침을 정의할 수 있습니다.

텍스트 배율, 변환 또는 회전 애니메이션을 적용할 때는 textMotion을 설정합니다. TextStyle의 매개변수를 TextMotion.Animated로 변경합니다. 이렇게 하면 텍스트 애니메이션 간 전환입니다. Modifier.graphicsLayer{ } 사용 텍스트를 변환하거나 회전하거나 크기를 조정할 수 있습니다

val infiniteTransition = rememberInfiniteTransition(label = "infinite transition")
val scale by infiniteTransition.animateFloat(
    initialValue = 1f,
    targetValue = 8f,
    animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse),
    label = "scale"
)
Box(modifier = Modifier.fillMaxSize()) {
    Text(
        text = "Hello",
        modifier = Modifier
            .graphicsLayer {
                scaleX = scale
                scaleY = scale
                transformOrigin = TransformOrigin.Center
            }
            .align(Alignment.Center),
        // Text composable does not take TextMotion as a parameter.
        // Provide it via style argument but make sure that we are copying from current theme
        style = LocalTextStyle.current.copy(textMotion = TextMotion.Animated)
    )
}

텍스트 색상 애니메이션 처리

단어
그림 10. 텍스트 색상에 애니메이션 적용 예
를 통해 개인정보처리방침을 정의할 수 있습니다.

텍스트 색상에 애니메이션을 적용하려면 BasicText 컴포저블에서 color 람다를 사용합니다. <ph type="x-smartling-placeholder">

val infiniteTransition = rememberInfiniteTransition(label = "infinite transition")
val animatedColor by infiniteTransition.animateColor(
    initialValue = Color(0xFF60DDAD),
    targetValue = Color(0xFF4285F4),
    animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse),
    label = "color"
)

BasicText(
    text = "Hello Compose",
    color = {
        animatedColor
    },
    // ...
)
</ph>

다양한 콘텐츠 유형 간 전환

&#39;다음&#39;이라고 말하는 녹색 화면
그림 11. AnimationContent를 사용하여 여러 컴포저블 간의 변경사항에 애니메이션 적용 (느리게)
를 통해 개인정보처리방침을 정의할 수 있습니다.

다음과 같은 경우 AnimatedContent를 사용하여 여러 컴포저블 간에 애니메이션을 적용합니다. 컴포저블 간에 표준 페이드를 적용하려면 Crossfade를 사용하세요.

var state by remember {
    mutableStateOf(UiState.Loading)
}
AnimatedContent(
    state,
    transitionSpec = {
        fadeIn(
            animationSpec = tween(3000)
        ) togetherWith fadeOut(animationSpec = tween(3000))
    },
    modifier = Modifier.clickable(
        interactionSource = remember { MutableInteractionSource() },
        indication = null
    ) {
        state = when (state) {
            UiState.Loading -> UiState.Loaded
            UiState.Loaded -> UiState.Error
            UiState.Error -> UiState.Loading
        }
    },
    label = "Animated Content"
) { targetState ->
    when (targetState) {
        UiState.Loading -> {
            LoadingScreen()
        }
        UiState.Loaded -> {
            LoadedScreen()
        }
        UiState.Error -> {
            ErrorScreen()
        }
    }
}

AnimatedContent은(는) 다양한 종류의 입력 및 사용할 수 있습니다. 자세한 내용은 AnimatedContent 또는 이 블로그 게시물을 읽어보세요. AnimatedContent

다른 목적지로 이동하는 동안 애니메이션 처리

두 컴포저블이 있습니다. 하나는 랜딩을, 다른 하나는 파란색으로 세부정보를 나타내고, 하나는 랜딩 컴포저블 위로 세부정보 컴포저블을 슬라이드하여 애니메이션입니다.
그림 12. Navigation-Compose를 사용하여 컴포저블 간 애니메이션
를 통해 개인정보처리방침을 정의할 수 있습니다.

다음을 사용할 때 컴포저블 간 전환에 애니메이션 적용 navigation-compose 아티팩트의 경우 enterTransition 및 컴포저블의 exitTransition 기본 애니메이션을 다음과 같이 설정할 수도 있습니다. 최상위 수준 NavHost의 모든 대상에 사용됩니다.

val navController = rememberNavController()
NavHost(
    navController = navController, startDestination = "landing",
    enterTransition = { EnterTransition.None },
    exitTransition = { ExitTransition.None }
) {
    composable("landing") {
        ScreenLanding(
            // ...
        )
    }
    composable(
        "detail/{photoUrl}",
        arguments = listOf(navArgument("photoUrl") { type = NavType.StringType }),
        enterTransition = {
            fadeIn(
                animationSpec = tween(
                    300, easing = LinearEasing
                )
            ) + slideIntoContainer(
                animationSpec = tween(300, easing = EaseIn),
                towards = AnimatedContentTransitionScope.SlideDirection.Start
            )
        },
        exitTransition = {
            fadeOut(
                animationSpec = tween(
                    300, easing = LinearEasing
                )
            ) + slideOutOfContainer(
                animationSpec = tween(300, easing = EaseOut),
                towards = AnimatedContentTransitionScope.SlideDirection.End
            )
        }
    ) { backStackEntry ->
        ScreenDetails(
            // ...
        )
    }
}

다양한 종류의 들어가기 및 나가기 전환이 적용됩니다. 자세히 알아보려면 문서를 참조하세요.

애니메이션 반복

두 색상 사이에 애니메이션이 적용되어 무한한 파란색 배경으로 변환되는 녹색 배경
그림 13. 두 값 사이에서 무한으로 움직이는 배경 색상
를 통해 개인정보처리방침을 정의할 수 있습니다.

infiniteRepeatable와 함께 rememberInfiniteTransition 사용 animationSpec를 누르면 애니메이션이 계속 반복됩니다. RepeatModes을(를) 다음으로 변경 어떻게 돌아가야 하는지 지정할 수 있습니다

finiteRepeatable를 사용하여 설정된 횟수를 반복합니다.

val infiniteTransition = rememberInfiniteTransition(label = "infinite")
val color by infiniteTransition.animateColor(
    initialValue = Color.Green,
    targetValue = Color.Blue,
    animationSpec = infiniteRepeatable(
        animation = tween(1000, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    ),
    label = "color"
)
Column(
    modifier = Modifier.drawBehind {
        drawRect(color)
    }
) {
    // your composable here
}

컴포저블 실행 시 애니메이션 시작

LaunchedEffect는 컴포저블이 컴포지션에 진입할 때 실행됩니다. 시작 컴포저블 실행 시 애니메이션이 표시되는 경우, 이를 사용하여 애니메이션을 유도할 수 있습니다. 상태를 변경할 수 있습니다 AnimatableanimateTo 메서드와 함께 사용하여 애니메이션 시작 시:

val alphaAnimation = remember {
    Animatable(0f)
}
LaunchedEffect(Unit) {
    alphaAnimation.animateTo(1f)
}
Box(
    modifier = Modifier.graphicsLayer {
        alpha = alphaAnimation.value
    }
)

순차 애니메이션 만들기

4개의 원 사이에 초록색 화살표가 움직이며 차례로 애니메이션됩니다.
그림 14. 순차 애니메이션이 진행되는 방식을 하나씩 보여주는 다이어그램

Animatable 코루틴 API를 사용하여 순차 또는 동시 실행 실행 있습니다. Animatable에서 animateTo를 순서대로 호출합니다. 각 애니메이션이 이전 애니메이션이 끝날 때까지 기다린 후에 다음 단계로 넘어갑니다 . 이는 정지 함수이기 때문입니다.

val alphaAnimation = remember { Animatable(0f) }
val yAnimation = remember { Animatable(0f) }

LaunchedEffect("animationKey") {
    alphaAnimation.animateTo(1f)
    yAnimation.animateTo(100f)
    yAnimation.animateTo(500f, animationSpec = tween(100))
}

동시 애니메이션 만들기

<ph type="x-smartling-placeholder">
</ph> 녹색 화살표가 있는 3개의 원에 각각 애니메이션이 적용되어 동시에 애니메이션이 적용됩니다.
그림 15. 동시 애니메이션이 진행되는 방식을 나타내는 다이어그램

코루틴 API (Animatable#animateTo() 또는 animate)를 사용합니다. Transition API를 사용하여 동시 애니메이션을 실행합니다. 여러 코루틴 컨텍스트에서 함수를 실행하고 동시에 애니메이션을 실행하는 방법 시간:

val alphaAnimation = remember { Animatable(0f) }
val yAnimation = remember { Animatable(0f) }

LaunchedEffect("animationKey") {
    launch {
        alphaAnimation.animateTo(1f)
    }
    launch {
        yAnimation.animateTo(100f)
    }
}

updateTransition API를 사용하여 동일한 상태를 사용하여 여러 속성 애니메이션을 동시에 구현할 수 있습니다. 아래 예에서는 상태 변경으로 제어되는 두 속성 rectborderWidth는 다음과 같습니다.

var currentState by remember { mutableStateOf(BoxState.Collapsed) }
val transition = updateTransition(currentState, label = "transition")

val rect by transition.animateRect(label = "rect") { state ->
    when (state) {
        BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f)
        BoxState.Expanded -> Rect(100f, 100f, 300f, 300f)
    }
}
val borderWidth by transition.animateDp(label = "borderWidth") { state ->
    when (state) {
        BoxState.Collapsed -> 1.dp
        BoxState.Expanded -> 0.dp
    }
}

애니메이션 성능 최적화

Compose의 애니메이션으로 인해 성능 문제가 발생할 수 있습니다. 이는 Kubernetes의 예를 들어 화면의 픽셀을 빠르게 이동하거나 변경하는 등 움직임의 착시 효과를 일으킵니다.

Compose의 다양한 단계(컴포지션, 레이아웃, 그리기)를 고려하세요. 만약 애니메이션이 레이아웃 단계를 변경하는 경우 영향을 받는 모든 컴포저블이 있습니다. 그리기 단계에서 애니메이션이 발생하면 기본적으로 레이아웃에서 애니메이션을 실행하는 경우보다 더 높은 성능을 발휘합니다. 전체적으로 해야 할 작업이 적기 때문입니다.

앱이 애니메이션하는 동안 가능한 한 적게 실행되도록 하려면 람다를 선택합니다. 가능하면 Modifier 버전을 사용하세요. 이렇게 하면 리컴포지션을 건너뛰고 컴포지션 단계를 벗어나는 애니메이션을 적용할 수 있습니다. 그렇지 않으면 Modifier.graphicsLayer{ } - 이 수정자는 항상 그리기에서 실행됨 준비 단계입니다 이에 대한 자세한 내용은 읽기 지연 섹션을 참고하세요. 참조하세요.

애니메이션 시간 변경

Compose는 기본적으로 대부분의 애니메이션에 스프링 애니메이션을 사용합니다. 스프링 또는 좀 더 자연스럽습니다. 또한 고정된 시간 대신 객체의 현재 속도를 고려합니다. 기본값을 재정의하려는 경우 위에서 설명한 모든 애니메이션 API가 animationSpec를 설정하여 애니메이션 실행 방식을 맞춤설정할 수 있습니다. 탄력성 있는 광고를 일정하게 만들 수 있습니다.

다음은 다양한 animationSpec 옵션을 요약한 것입니다.

  • spring: 물리학 기반 애니메이션으로, 모든 애니메이션의 기본값입니다. 나 강성 또는 dampingRatio를 변경하여 다른 애니메이션을 만들 수 있습니다. 있습니다.
  • tween (between의 줄임말): 지속 시간 기반 애니메이션으로, 애니메이션 처리 Easing 함수로 바꾸세요.
  • keyframes: 애니메이션을 적용할 수 있습니다.
  • repeatable: 특정 횟수만큼 실행되는 기간 기반 사양입니다. RepeatMode에 의해 지정됩니다.
  • infiniteRepeatable: 영구적으로 실행되는 기간 기반 사양입니다.
  • snap: 애니메이션 없이 즉시 종료 값에 맞춰집니다.
를 통해 개인정보처리방침을 정의할 수 있습니다. <ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">여기에 대체 텍스트를 작성하세요.</ph>
그림 16. 설정된 사양이 없고 설정된 커스텀 Spring 사양 비교
를 통해 개인정보처리방침을 정의할 수 있습니다.

animationSpecs에 대한 자세한 내용은 전체 문서를 참조하세요.

추가 리소스

Compose의 재미있는 애니메이션에 관한 더 많은 예는 다음을 참고하세요.