Краткое руководство по анимации в Compose

Compose имеет множество встроенных механизмов анимации, и может быть сложно понять, какой из них выбрать. Ниже приведен список распространенных случаев использования анимации. Более подробную информацию о полном наборе различных доступных вам опций API можно найти в полной документации Compose Animation .

Анимация общих составных свойств

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
        }

) {
}

Вы также можете использовать AnimatedContent с SizeTransform , чтобы описать, как должны происходить изменения размера.

Анимировать положение составных элементов

Зеленый составной элемент плавно анимируется вниз и вправо.
Рисунок 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{ } . Этот модификатор передает изменения размера и положения родительскому элементу, что затем влияет на других дочерних элементов.

Например, если вы перемещаете Box внутри Column , а другим дочерним элементам необходимо перемещаться при перемещении 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)
    )
}

2 блока, второй блок анимирует свое положение X, Y, третий блок реагирует, перемещаясь также на величину Y.
Рисунок 6. Анимация с помощью Modifier.layout{ }

Анимация заполнения составного элемента

Зеленый составной элемент становится меньше и больше при нажатии, а отступы анимируются.
Рис. 7. Composable с анимацией отступов

Чтобы анимировать заполнение составного объекта, используйте 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
        }
)

Анимация возвышения составного объекта

Рисунок 8. Анимация высоты Composable при нажатии

Чтобы анимировать подъем составного объекта, используйте 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. Пример анимации цвета текста.

Чтобы анимировать цвет текста, используйте лямбда color в составном элементе BasicText :

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
    },
    // ...
)

Переключайтесь между различными типами контента

Зеленый экран говорит
Рис. 11. Использование AnimatedContent для анимации изменений между различными составными объектами (замедленно)

Используйте 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

Чтобы анимировать переходы между составными объектами при использовании артефакта навигации-компонования , укажите 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. Анимация цвета фона между двумя значениями, бесконечно

Используйте rememberInfiniteTransition с animationSpec infiniteRepeatable , чтобы постоянно повторять анимацию. Измените 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 запускается, когда компонуемый объект входит в композицию. Он запускает анимацию при запуске составного объекта, вы можете использовать это для изменения состояния анимации. Использование Animatable с методом animateTo для запуска анимации при запуске:

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

Создание последовательных анимаций

Четыре круга с зелеными стрелками, которые анимируются один за другим.
Рисунок 14. Диаграмма, показывающая, как выполняется последовательная анимация, одна за другой.

Используйте API-интерфейсы сопрограмм Animatable для выполнения последовательной или параллельной анимации. Вызов animateTo в Animatable один за другим приводит к тому, что каждая анимация ожидает завершения предыдущей анимации, прежде чем продолжить. Это потому, что это функция приостановки.

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

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

Создание параллельных анимаций

Три круга с зелеными стрелками, анимирующими каждый из них, анимирующими все вместе одновременно.
Рисунок 15. Диаграмма, показывающая, как выполняются одновременные анимации.

Используйте API-интерфейсы сопрограмм ( Animatable#animateTo() или animate ) или API- Transition для достижения одновременной анимации. Если вы используете несколько функций запуска в контексте сопрограммы, анимация запускается одновременно:

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

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

Вы можете использовать API updateTransition , чтобы использовать одно и то же состояние для одновременного управления множеством различных анимаций свойств. В приведенном ниже примере анимируются два свойства, управляемые изменением состояния: rect и borderWidth :

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 могут вызвать проблемы с производительностью. Это связано с природой анимации: быстрое перемещение или изменение пикселей на экране, покадрово, чтобы создать иллюзию движения.

Рассмотрим различные этапы Compose : композицию, макет и рисование. Если ваша анимация меняет фазу макета, она требует, чтобы все затронутые составные элементы были перерисованы и перерисованы. Если ваша анимация происходит на этапе рисования, она по умолчанию будет более производительной, чем если бы вы запускали анимацию на этапе макета, поскольку в целом ей придется выполнить меньше работы.

Чтобы ваше приложение выполняло как можно меньше действий при анимации, по возможности выбирайте лямбда-версию Modifier . Это пропускает рекомпозицию и выполняет анимацию вне фазы композиции, в противном случае используйте Modifier.graphicsLayer{ } , поскольку этот модификатор всегда выполняется на этапе рисования. Дополнительные сведения об этом см. в разделе «Отложенное чтение» документации по производительности.

Изменить время анимации

Compose по умолчанию использует пружинную анимацию для большинства анимаций. Пружины или анимация, основанная на физике, кажутся более естественными. Их также можно прерывать, поскольку они учитывают текущую скорость объекта, а не фиксированное время. Если вы хотите переопределить значение по умолчанию, все API-интерфейсы анимации, показанные выше, имеют возможность установить animationSpec , чтобы настроить способ запуска анимации, хотите ли вы, чтобы она выполнялась в течение определенной продолжительности или была более динамичной.

Ниже приводится сводка различных параметров animationSpec :

  • spring : анимация, основанная на физике, используется по умолчанию для всех анимаций. Вы можете изменить жесткость или DampingRatio, чтобы добиться другого внешнего вида анимации.
  • tween (сокращение от Between ): анимация на основе продолжительности, анимация между двумя значениями с помощью функции Easing .
  • keyframes : спецификация для указания значений в определенных ключевых точках анимации.
  • repeatable : спецификация на основе продолжительности, которая запускается определенное количество раз, указанное в RepeatMode .
  • infiniteRepeatable : спецификация на основе продолжительности, которая работает вечно.
  • snap : мгновенно привязывается к конечному значению без какой-либо анимации.
Напишите здесь свой альтернативный текст
Рисунок 16. Набор спецификаций без набора спецификаций и набор спецификаций Custom Spring

Прочтите полную документацию для получения дополнительной информации об анимационных спецификациях .

Дополнительные ресурсы

Дополнительные примеры забавной анимации в Compose можно найти здесь:

,

Compose имеет множество встроенных механизмов анимации, и может быть сложно понять, какой из них выбрать. Ниже приведен список распространенных случаев использования анимации. Более подробную информацию о полном наборе различных доступных вам опций API можно найти в полной документации Compose Animation .

Анимация общих составных свойств

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
        }

) {
}

Вы также можете использовать AnimatedContent с SizeTransform , чтобы описать, как должны происходить изменения размера.

Анимировать положение составных элементов

Зеленый составной элемент плавно анимируется вниз и вправо.
Рисунок 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{ } . Этот модификатор передает изменения размера и положения родительскому элементу, что затем влияет на других дочерних элементов.

Например, если вы перемещаете Box внутри Column , а другим дочерним элементам необходимо перемещаться при перемещении 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)
    )
}

2 блока, второй блок анимирует свое положение X, Y, третий блок реагирует, перемещаясь также на величину Y.
Рисунок 6. Анимация с помощью Modifier.layout{ }

Анимация заполнения составного элемента

Зеленый составной элемент становится меньше и больше при нажатии, а отступы анимируются.
Рис. 7. Composable с анимацией отступов

Чтобы анимировать заполнение составного объекта, используйте 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
        }
)

Анимация возвышения составного объекта

Рисунок 8. Анимация высоты Composable при нажатии

Чтобы анимировать подъем составного объекта, используйте 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. Пример анимации цвета текста.

Чтобы анимировать цвет текста, используйте лямбда color в составном элементе BasicText :

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
    },
    // ...
)

Переключайтесь между различными типами контента

Зеленый экран говорит
Рис. 11. Использование AnimatedContent для анимации изменений между различными составными объектами (замедленно)

Используйте 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

Чтобы анимировать переходы между составными объектами при использовании артефакта навигации-компонования , укажите 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. Анимация цвета фона между двумя значениями, бесконечно

Используйте rememberInfiniteTransition с animationSpec infiniteRepeatable , чтобы постоянно повторять анимацию. Измените 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 запускается, когда компонуемый объект входит в композицию. Он запускает анимацию при запуске составного объекта, вы можете использовать это для изменения состояния анимации. Использование Animatable с методом animateTo для запуска анимации при запуске:

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

Создание последовательных анимаций

Четыре круга с зелеными стрелками, которые анимируются один за другим.
Рисунок 14. Диаграмма, показывающая, как выполняется последовательная анимация, одна за другой.

Используйте API-интерфейсы сопрограмм Animatable для выполнения последовательной или параллельной анимации. Вызов animateTo в Animatable один за другим приводит к тому, что каждая анимация ожидает завершения предыдущей анимации, прежде чем продолжить. Это потому, что это функция приостановки.

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

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

Создание параллельных анимаций

Три круга с зелеными стрелками, анимирующими каждый из них, анимирующими все вместе одновременно.
Рисунок 15. Диаграмма, показывающая, как выполняются одновременные анимации.

Используйте API-интерфейсы сопрограмм ( Animatable#animateTo() или animate ) или API- Transition для достижения одновременной анимации. Если вы используете несколько функций запуска в контексте сопрограммы, анимация запускается одновременно:

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

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

Вы можете использовать API updateTransition , чтобы использовать одно и то же состояние для одновременного управления множеством различных анимаций свойств. В приведенном ниже примере анимируются два свойства, управляемые изменением состояния: rect и borderWidth :

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 могут вызвать проблемы с производительностью. Это связано с природой анимации: быстрое перемещение или изменение пикселей на экране, покадрово, чтобы создать иллюзию движения.

Рассмотрим различные этапы Compose : композицию, макет и рисование. Если ваша анимация меняет фазу макета, она требует, чтобы все затронутые составные элементы были перерисованы и перерисованы. Если ваша анимация происходит на этапе рисования, она по умолчанию будет более производительной, чем если бы вы запускали анимацию на этапе макета, поскольку в целом ей придется выполнить меньше работы.

Чтобы ваше приложение выполняло как можно меньше действий при анимации, по возможности выбирайте лямбда-версию Modifier . Это пропускает рекомпозицию и выполняет анимацию вне фазы композиции, в противном случае используйте Modifier.graphicsLayer{ } , поскольку этот модификатор всегда выполняется на этапе рисования. Дополнительные сведения об этом см. в разделе «Отложенное чтение» документации по производительности.

Изменить время анимации

Compose по умолчанию использует пружинную анимацию для большинства анимаций. Пружины или анимация, основанная на физике, кажутся более естественными. Их также можно прерывать, поскольку они учитывают текущую скорость объекта, а не фиксированное время. Если вы хотите переопределить значение по умолчанию, все API-интерфейсы анимации, показанные выше, имеют возможность установить animationSpec , чтобы настроить способ запуска анимации, хотите ли вы, чтобы она выполнялась в течение определенной продолжительности или была более динамичной.

Ниже приводится сводка различных параметров animationSpec :

  • spring : анимация, основанная на физике, используется по умолчанию для всех анимаций. Вы можете изменить жесткость или DampingRatio, чтобы добиться другого внешнего вида анимации.
  • tween (сокращение от Between ): анимация на основе продолжительности, анимация между двумя значениями с помощью функции Easing .
  • keyframes : спецификация для указания значений в определенных ключевых точках анимации.
  • repeatable : спецификация на основе продолжительности, которая запускается определенное количество раз, указанное в RepeatMode .
  • infiniteRepeatable : спецификация на основе продолжительности, которая работает вечно.
  • snap : мгновенно привязывается к конечному значению без какой-либо анимации.
Напишите здесь свой альтернативный текст
Рисунок 16. Набор спецификаций без набора спецификаций и набор спецификаций Custom Spring

Прочтите полную документацию для получения дополнительной информации об анимационных спецификациях .

Дополнительные ресурсы

Дополнительные примеры забавной анимации в Compose можно найти здесь:

,

Compose имеет множество встроенных механизмов анимации, и может быть сложно понять, какой из них выбрать. Ниже приведен список распространенных случаев использования анимации. Более подробную информацию о полном наборе различных доступных вам опций API можно найти в полной документации Compose Animation .

Анимация общих составных свойств

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
        }

) {
}

Вы также можете использовать AnimatedContent с SizeTransform , чтобы описать, как должны происходить изменения размера.

Анимировать положение составных элементов

Зеленый составной элемент плавно анимируется вниз и вправо.
Рисунок 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{ } . Этот модификатор передает изменения размера и положения родительскому элементу, что затем влияет на других дочерних элементов.

Например, если вы перемещаете Box внутри Column , а другим дочерним элементам необходимо перемещаться при перемещении 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)
    )
}

2 блока, второй блок анимирует свое положение X, Y, третий блок реагирует, перемещаясь также на величину Y.
Рисунок 6. Анимация с помощью Modifier.layout{ }

Анимация заполнения составного элемента

Зеленый составной элемент становится меньше и больше при нажатии, а отступы анимируются.
Рис. 7. Composable с анимацией отступов

Чтобы анимировать заполнение составного объекта, используйте 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
        }
)

Анимация возвышения составного объекта

Рисунок 8. Анимация высоты Composable при нажатии

Чтобы анимировать подъем составного объекта, используйте 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. Пример анимации цвета текста.

Чтобы анимировать цвет текста, используйте лямбда color в составном элементе BasicText :

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
    },
    // ...
)

Переключайтесь между различными типами контента

Зеленый экран говорит
Рис. 11. Использование AnimatedContent для анимации изменений между различными составными объектами (замедленно)

Используйте 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

Чтобы анимировать переходы между составными объектами при использовании артефакта навигации-компонования , укажите 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. Анимация цвета фона между двумя значениями, бесконечно

Используйте rememberInfiniteTransition с animationSpec infiniteRepeatable , чтобы постоянно повторять анимацию. Измените 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 запускается, когда компонуемый объект входит в композицию. Он запускает анимацию при запуске составного объекта, вы можете использовать это для изменения состояния анимации. Использование Animatable с методом animateTo для запуска анимации при запуске:

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

Создание последовательных анимаций

Четыре круга с зелеными стрелками, которые анимируются один за другим.
Рисунок 14. Диаграмма, показывающая, как выполняется последовательная анимация, одна за другой.

Используйте API-интерфейсы сопрограмм Animatable для выполнения последовательной или параллельной анимации. Вызов animateTo в Animatable один за другим приводит к тому, что каждая анимация ожидает завершения предыдущей анимации, прежде чем продолжить. Это потому, что это функция приостановки.

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

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

Создание параллельных анимаций

Три круга с зелеными стрелками, анимирующими каждый из них, анимирующими все вместе одновременно.
Рисунок 15. Диаграмма, показывающая, как выполняются одновременные анимации.

Используйте API-интерфейсы сопрограмм ( Animatable#animateTo() или animate ) или API- Transition для достижения одновременной анимации. Если вы используете несколько функций запуска в контексте сопрограммы, анимация запускается одновременно:

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

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

Вы можете использовать API updateTransition , чтобы использовать одно и то же состояние для одновременного управления множеством различных анимаций свойств. В приведенном ниже примере анимируются два свойства, управляемые изменением состояния: rect и borderWidth :

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 могут вызвать проблемы с производительностью. Это связано с природой анимации: быстрое перемещение или изменение пикселей на экране, покадрово, чтобы создать иллюзию движения.

Рассмотрим различные этапы Compose : композицию, макет и рисование. Если ваша анимация меняет фазу макета, она требует, чтобы все затронутые составные элементы были перерисованы и перерисованы. Если ваша анимация происходит на этапе рисования, по умолчанию она будет более производительной, чем если бы вы запускали анимацию на этапе макета, поскольку в целом ей придется выполнить меньше работы.

Чтобы ваше приложение выполняло как можно меньше действий при анимации, по возможности выбирайте лямбда-версию Modifier . Это пропускает переоборудование и выполняет анимацию за пределами фазы композиции, в противном случае используйте Modifier.graphicsLayer{ } , поскольку этот модификатор всегда работает в фазе рисования. Для получения дополнительной информации об этом см. В разделе «Открытые чтения» в документации производительности.

Изменить время анимации

Создание по умолчанию использует пружинные анимации для большинства анимаций. Пружины, или анимация на основе физики, чувствуют себя более естественными. Они также прерывают, поскольку они принимают во внимание токовую скорость объекта, а не фиксированное время. Если вы хотите переопределить по умолчанию, все API, продемонстрированные выше анимации, имеют возможность установить animationSpec для настройки того, как работает анимация, независимо от того, хотите ли вы, чтобы она выполнялась в течение определенной продолжительности или быть более удушенным.

Ниже приведено краткое изложение различных параметров animationSpec :

  • spring : Физика, основанная на анимации, по умолчанию для всех анимаций. Вы можете изменить жесткость или демпфинг, чтобы достичь другого анимационного внешнего вида.
  • tween (короткая для международной ): анимация на основе продолжительности, анимирует между двумя значениями с функцией Easing .
  • keyframes : спецификация для определения значений в определенных ключевых точках в анимации.
  • repeatable : спецификация на основе продолжительности, которая работает определенное количество раз, указанная RepeatMode .
  • infiniteRepeatable : Spec на основе продолжительности, которая работает навсегда.
  • snap : Мгновенно прижимает к конечному значению без какой -либо анимации.
Напишите здесь свой альт -текст
Рисунок 16. Нет набора спецификаций против пользовательского набора Spring Spect

Прочитайте полную документацию для получения дополнительной информации о анимациях .

Дополнительные ресурсы

Для получения дополнительных примеров веселой анимации в композите, посмотрите на следующее:

,

Compose имеет много встроенных механизмов анимации, и это может быть ошеломляющим знать, какой из них выбрать. Ниже приведен список общих вариантов использования анимации. Для получения более подробной информации о полном наборе различных параметров API, доступных для вас, прочитайте полную документацию по анимации .

Оживить общие композиционные свойства

Compose предоставляет удобные API, которые позволяют вам решать для многих общих вариантов использования анимации. Этот раздел демонстрирует, как вы можете оживить общие свойства композиции.

Оживление / исчезновение

Зеленый композиционный показ и прячутся
Рисунок 1. Анимируя внешний вид и исчезновение элемента в столбце

Используйте AnimatedVisibility , чтобы скрыть или показать композицию. Дети внутри AnimatedVisibility могут использовать Modifier.animateEnterExit() для своего собственного перехода Enter или выхода.

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
        }

) {
}

Вы также можете использовать AnimatedContent , с SizeTransform чтобы описать, как должны происходить изменения размера.

Одушевленное положение композиционного

Зеленый композитный плавно анимируя вниз и справа
Рисунок 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{ } . Этот модификатор распространяет размер и изменения позиции в отношении родителей, что затем затрагивает других детей.

Например, если вы перемещаете Box в Column , а другим детям нужно перемещаться при 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)
    )
}

2 коробки со 2 -й поле, анимирующим его позицию 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
        }
)

Оживить поднятие композиционного

Рисунок 8. Анимирование поднятия Composable на щелчке

Чтобы оживить повышение композиции, используйте 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 To 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. Пример, показывающий анимирующий цвет текста

Чтобы анимировать цвет текста, используйте color лямбду на BasicText композитном:

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
    },
    // ...
)

Переключаться между различными типами контента

Зеленый экран, говорящий
Рисунок 11. Использование AnimatedContent для аниматических изменений между различными композициями (замедленное вниз)

Используйте 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. Анимация между композициями с использованием навигационного состава

Чтобы оживить переходы между композиционными продуктами при использовании артефакта на навигации , укажите 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. Цвет фона, анимирующий между двумя значениями, бесконечно

Используйте rememberInfiniteTransition с infiniteRepeatable 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 запускается, когда композитный входит в композицию. Он запускает анимацию при запуске композиции, вы можете использовать это, чтобы стимулировать изменение состояния анимации. Используя Animatable с помощью метода animateTo для запуска анимации при запуске:

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

Создайте последовательные анимации

Четыре круга с зелеными стрелками, анимирующими между каждым, анимируя один за другим.
Рисунок 14. Диаграмма, указывающая, как прогрессирует последовательная анимация, одна за другой.

Используйте Animatable API -интерфейсы Coroutine для выполнения последовательных или одновременных анимаций. Вызов animateTo на Animatable после другого заставляет каждую анимацию ждать, пока предыдущая анимация закончится, прежде чем продолжить. Это потому, что это подвесная функция.

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

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

Создать одновременные анимации

Три круга с зелеными стрелами, анимирующими для каждого из них, анимируя все вместе.
Рисунок 15. Диаграмма, указывающая на то, как продвигается одновременно одновременно.

Используйте API -интерфейсы COROUTINE ( Animatable#animateTo() или animate ) или API Transition для достижения одновременной анимации. Если вы используете несколько функций запуска в контексте COROUTINE, он запускает анимации одновременно:

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

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

Вы можете использовать API updateTransition , чтобы использовать одно и то же состояние, чтобы одновременно стимулировать множество различных анимаций свойств. Пример ниже анимирует два свойства, контролируемые изменением состояния, rect и borderWidth :

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
    }
}

Оптимизировать производительность анимации

Анимации в составе могут вызвать проблемы с производительностью. Это связано с характером того, что такое анимация: быстрое перемещение или изменение пикселей на экране, кадр за полным рамой, чтобы создать иллюзию движения.

Рассмотрим различные фазы композиции : композиция, макет и нарисуйте. Если ваша анимация изменяет этап макета, она требует всех затронутых композиционных продуктов для ретранту и перераспределения. Если ваша анимация возникает на этапе рисования, по умолчанию она будет более эффективной, чем если бы вы запустили анимацию на этапе макета, так как у него будет меньше работы, чтобы выполнить в целом.

Чтобы убедиться, что ваше приложение делает как можно меньше при анимации, выберите версию Modifier Lambda, где это возможно. Это пропускает переоборудование и выполняет анимацию за пределами фазы композиции, в противном случае используйте Modifier.graphicsLayer{ } , поскольку этот модификатор всегда работает в фазе рисования. Для получения дополнительной информации об этом см. В разделе «Открытые чтения» в документации производительности.

Изменить время анимации

Создание по умолчанию использует пружинные анимации для большинства анимаций. Пружины, или анимация на основе физики, чувствуют себя более естественными. Они также прерывают, поскольку они принимают во внимание токовую скорость объекта, а не фиксированное время. Если вы хотите переопределить по умолчанию, все API, продемонстрированные выше анимации, имеют возможность установить animationSpec для настройки того, как работает анимация, независимо от того, хотите ли вы, чтобы она выполнялась в течение определенной продолжительности или быть более удушенным.

Ниже приведено краткое изложение различных параметров animationSpec :

  • spring : Физика, основанная на анимации, по умолчанию для всех анимаций. Вы можете изменить жесткость или демпфинг, чтобы достичь другого анимационного внешнего вида.
  • tween (короткая для международной ): анимация на основе продолжительности, анимирует между двумя значениями с функцией Easing .
  • keyframes : спецификация для определения значений в определенных ключевых точках в анимации.
  • repeatable : спецификация на основе продолжительности, которая работает определенное количество раз, указанная RepeatMode .
  • infiniteRepeatable : Spec на основе продолжительности, которая работает навсегда.
  • snap : Мгновенно прижимает к конечному значению без какой -либо анимации.
Напишите здесь свой альт -текст
Рисунок 16. Нет набора спецификаций против пользовательского набора Spring Spect

Прочитайте полную документацию для получения дополнительной информации о анимациях .

Дополнительные ресурсы

Для получения дополнительных примеров веселой анимации в композите, посмотрите на следующее: