Функция tween. Время и сглаживание анимации

Последнее обновление: 12.04.2024

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

Например, рассмотренная в прощлой теме функция animateDpAsState(), которая выполняет анимацию значений Dp, также имеет подобный параметр:

@Composable
public fun animateDpAsState(
    targetValue: Dp,
    animationSpec: AnimationSpec<Dp> = dpDefaultSpring,   // установка анимации
    label: String = "DpAnimation",
    finishedListener: ((Dp) -> Unit)? = null
): State<Dp>

Для создания объекта AnimationSpec Jetpack Compose предоставляет множество специальных функций и значений. Рассмотрим наиболее используемую из них - функцию tween(). Она имеет следующую сигнатуру:

public fun <T> tween(
    durationMillis: Int = DefaultDurationMillis,
    delayMillis: Int = 0,
    easing: Easing = FastOutSlowInEasing
): TweenSpec<T>

Она принимает следующие параметры:

  • durationMillis: длительность анимации в миллисекундах

  • delayMillis: устанавливает начальную задержку

  • easing: позволяет ускорять и замедлять анимацию

Наиболее распространенный сценарий использования функции tween() - настройка времени анимации. Например:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.platform.LocalConfiguration


class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val boxWidth = 150      // ширина компонента
            val startOffset = 0   // начальная позиция
            val endOffset = LocalConfiguration.current.screenWidthDp - boxWidth // конечная позиция

            var boxState by remember { mutableStateOf(startOffset)}

            val offset by animateDpAsState(
                targetValue = boxState.dp,
                animationSpec = tween(durationMillis = 5000) // анимация длится 5 секунд
            )

            Column(Modifier.fillMaxWidth()) {
                Box(Modifier.padding(start=offset).size(boxWidth.dp).background(Color.DarkGray))

                Button({boxState = if (boxState==startOffset) {endOffset} else {startOffset} },
                    Modifier.padding(10.dp)) {
                    Text("Move", fontSize = 25.sp)
                }
            }
        }
    }
}

В данном случае по нажатию на кнопку происходит перемещение компонента с помощью анимации значений Dp:

animateDpAsState и анимация Dp в Jetpack Compose на Kotlin в Android

Здесь в начале определяем некоторые значения, которые будут вычисляться для изменения позиции компонента Box:

val boxWidth = 150      // ширина компонента
val startOffset = 0   // начальная позиция
val endOffset = LocalConfiguration.current.screenWidthDp - boxWidth // конечная позиция

То есть наш Box имеет ширину boxWidth и будет перемещаться от позиции startOffset до endOffset.

Далее определяем состояние, от которого будет зависеть позиция компонента Box:

var boxState by remember { mutableStateOf(startOffset)}

Затем определяем анимацию позиции Box с помощью функции animateDpAsState()

val offset by animateDpAsState(
    targetValue = boxState.dp,
    animationSpec = tween(durationMillis = 5000) // анимация длится 5 секунд
)

Параметр targetValue на основании boxState определяет позицию, к которой надо выполнить переход. А параметру animationSpec присваивается значение функции tween(), в которую передается число 5000. То есть анимация будет длиться 5 миллисекунд.

Результат функцит animateDpAsState() передаем в переменну offset и далее используем ее для установки отступа от начала контейнера для компонента Box:

Box(Modifier.padding(start=offset)      // устанавливаем отступ
        .size(boxWidth.dp)
        .background(Color.DarkGray))

И для запуска анимации на кнопку Move вешаем обработчик нажатия, который переключает значение boxState со startOffset на endOffset и обратно.

Button({boxState = if (boxState==startOffset) {endOffset} else {startOffset} },

easing

Параметр easing в функции tween() позволяет ускорять и замедлять анимацию. В качестве значения можно передать одно из значений, определенных в пакете androidx.compose.animation.core.:

  • FastOutSlowInEasing

  • LinearOutSlowInEasing

  • FastOutLinearEasing

  • LinearEasing

  • CubicBezierEasing

Рассмотрим их применение:

package com.example.helloapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.platform.LocalConfiguration


class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val boxWidth = 150
            val startOffset = 0
            val endOffset = LocalConfiguration.current.screenWidthDp - boxWidth

            var boxState by remember { mutableStateOf(startOffset)}

            val offset by animateDpAsState(
                targetValue = boxState.dp,
                animationSpec = tween(durationMillis = 5000, easing = FastOutSlowInEasing) // анимация длится 5 секунд
            )
            val offset1 by animateDpAsState(
                targetValue = boxState.dp,
                animationSpec = tween(durationMillis = 5000, easing = LinearOutSlowInEasing) // анимация длится 5 секунд
            )

            Column(Modifier.fillMaxWidth()) {
                Box(Modifier.padding(start=offset).size(boxWidth.dp).background(Color.DarkGray))
                Box(Modifier.padding(start=offset1, top=10.dp).size(boxWidth.dp).background(Color.DarkGray))

                Button({boxState = if (boxState==startOffset) {endOffset} else {startOffset} },
                    Modifier.padding(10.dp)) {
                    Text("Move", fontSize = 25.sp)
                }
            }
        }
    }
}

В данном случае я добавил второй компонент Box для сравнения. Для каждого из них применяется анимация в течение 5 секунд, однако значение параметров easing отличается:

val offset by animateDpAsState(
    targetValue = boxState.dp,
    animationSpec = tween(durationMillis = 5000, easing = FastOutSlowInEasing) // анимация длится 5 секунд
)
val offset1 by animateDpAsState(
    targetValue = boxState.dp,
    animationSpec = tween(durationMillis = 5000, easing = LinearOutSlowInEasing) // анимация длится 5 секунд
)

Что повлияет на принцип изменение отступа:

animateDpAsState и tween easing в Jetpack Compose на Kotlin в Android

Если для параметра easing применяется значение CubicBezierEasing, то в конструктор надо передать координаты двух контрольных точек кривой Безье, на основании которой расчитывается анимация:

import androidx.compose.animation.core.CubicBezierEasing
.......................
 val offset by animateDpAsState(
    targetValue = boxState.dp,
    animationSpec = tween(durationMillis = 5000, easing = CubicBezierEasing(0f, 1f, 0.5f,1f)) 
)
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850