AnimationSpec para el aprendizaje de animación de Compose

En el artículo anterior, en la API de animación lista para usar de Compose , en el proceso de aprendizaje y uso de la animación de composición, hay un atributo de parámetro que siempre existe, que es AnimationSpec. Ya sea animate*AsState para agregar efectos de animación a un solo valor, o updateTransition para agregar efectos de animación a múltiples valores, o algunas API de animación de alto nivel empaquetadas: animationContentSize, AnimatedVisibility, todas tienen AnimationSpec esta propiedad de parámetro. Pero si no comprende y no sabe cómo usar AnimationSpec, no le impedirá usar estas API de animación, porque estas API de animación proporcionan implementaciones de AnimationSpec predeterminadas.

interface AnimationSpec<T> {
    fun <V : AnimationVector> vectorize(
        converter: TwoWayConverter<T, V>
    ): VectorizedAnimationSpec<V>
}

AnimationSpec, la especificación de una animación, es una interfaz utilizada para almacenar especificaciones de animación, incluido el tipo de datos que se animará y la configuración de animación que se utilizará después de convertir los datos en animación.
Compose ya implementa algunas AnimationSpecs de uso común para nosotros.
Especificaciones de animación
El funcionario proporciona 8 tipos que se pueden usar directamente (los marcados en azul son clases, que se pueden usar directamente).

SpringSpec

SpringSpec, animación elástica, es la implementación predeterminada de AnimationSpec para muchas animaciones, como animate*AsState, updateTransition, animationContentSize, etc.

@Stable
fun <T> spring(
    dampingRatio: Float = Spring.DampingRatioNoBouncy,
    stiffness: Float = Spring.StiffnessMedium,
    visibilityThreshold: T? = null
): SpringSpec<T> =
    SpringSpec(dampingRatio, stiffness, visibilityThreshold)

El método oficial spring() se usa para construir SpringSpec, que recibe tres parámetros, pero todos tienen sus valores predeterminados.

  • dampingRatio: relación de amortiguamiento, el valor por defecto es Spring.DampingRatioNoBouncy = 1f, es decir, no hay elasticidad. Cuando la relación de amortiguamiento es <1, cuanto menor sea la relación de amortiguamiento, más elástico será el resorte.
  • rigidez: Rigidez, el valor predeterminado es Spring.StiffnessMedium.
  • VisibilityThreshold: umbral de visibilidad.

Tanto la relación de amortiguación como la rigidez tienen 5 valores de enumeración cada una.

@Composable
fun SpringDemo() {
    var small by remember {
        mutableStateOf(true)
    }
    val size: Dp by animateDpAsState(
        targetValue = if (small) 40.dp else 100.dp,
        animationSpec = spring(
            dampingRatio = Spring.DampingRatioNoBouncy,
            stiffness = Spring.StiffnessHigh
        )
    )
    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(onClick = { small = !small }) {
            Text(text = "改变方块大小-spring")
        }
        Box(
            modifier = Modifier
                .size(size)
                .background(Color.LightGray)
        )
    }
}

Usamos una animación de tamaño para observar el efecto SpringSpec. Modifique los parámetros de resorte del código anterior para observar varios efectos SpringSpec. En el mismo dampingRatio = Spring.DampingRatioNoBouncy, es decir, cuando no hay elasticidad, cuanto menor es la rigidez, más tiempo el tiempo de animación El efecto de animación es más obvio. A rigideces más bajas, cuanto menor sea la relación de amortiguación, más pronunciada será la animación de rebote.

interpolación

TweenSpec, utilizado para crear una especificación de animación configurada con la duración, el tiempo de retraso y la curva de aceleración determinados.

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

El método oficial tween() se usa para construir TweenSpec, que recibe tres parámetros, pero todos tienen sus valores predeterminados.

  • durationMillis: duración de la animación, el valor por defecto es de 300 milisegundos.
  • delayMillis: tiempo de retraso de la animación, el valor por defecto es 0, es decir, la animación empieza inmediatamente.
  • aceleración: cambio de curva de animación, el valor predeterminado es FastOutSlowInEasing
@Stable
fun interface Easing {
    fun transform(fraction: Float): Float
}

Easing es una interfaz, que es un método para ajustar la fracción de animación, lo que permite que los elementos de transición aceleren o desaceleren en lugar de moverse a una velocidad constante. Hay un método en Easing, y la fracción de parámetro en el método es un valor entre 0 y 1.0 Indica el punto de progreso actual en la animación, donde 0 significa inicio y 1.0 significa final.
Easing tiene solo una clase de implementación, CubicBezierEasing, que implementa una curva Bezier de tercer orden. La recomendación oficial es que, en lugar de crear una nueva instancia, considere usar un Easing cúbico común.
El funcionario proporciona cuatro implementaciones de Easing comúnmente utilizadas:

//以静止开始和结束的元素使用此标准缓动。 他们快速加速并逐渐减速,以强调过渡的结束。这是最常见的方式。这相当于原生安卓插值器FastOutSlowInInterpolator
val FastOutSlowInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)
//传入的元素使用减速缓动进行动画处理,它以峰值速度(元素运动的最快点)开始过渡并在静止时结束。这相当于原生安卓插值器LinearOutSlowInInterpolator
val LinearOutSlowInEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)
//退出屏幕的元素使用加速缓动,它们从静止开始并以峰值速度结束。这相当于原生安卓插值器FastOutLinearInInterpolator
val FastOutLinearInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 1.0f, 1.0f)
//线性、匀速缓动
val LinearEasing: Easing = Easing { fraction -> fraction }

Cambiamos el caso en SpringSpec para usar spring para usar interpolación para observar el efecto TweenSpec, y modificamos los parámetros en la interpolación del código para observar varios efectos TweenSpec. Para poder observar mejor el efecto de la animación, el tiempo de animación se puede establecer de forma adecuada durante más tiempo.

@Composable
fun TweenDemo() {
    var small by remember {
        mutableStateOf(true)
    }
    val size: Dp by animateDpAsState(
        targetValue = if (small) 40.dp else 100.dp,
        animationSpec = tween(
            durationMillis = 3000,
            easing = FastOutSlowInEasing
        )
    )
    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(onClick = { small = !small }) {
            Text(text = "改变方块大小-tween")
        }
        Box(
            modifier = Modifier
                .size(size)
                .background(Color.LightGray)
        )
    }
}

SnapSpec

SnapSpec, que describe un tipo de animación de corte de salto. Inmediatamente ajusta el valor animado al valor final.

@Stable
fun <T> snap(delayMillis: Int = 0) = SnapSpec<T>(delayMillis)

El método oficial snap() se usa para construir un SnapSpec. Solo hay un parámetro, delayMillis: el tiempo de retraso de la animación. El valor predeterminado es 0, es decir, la animación comienza inmediatamente.

@Immutable
class SnapSpec<T>(val delay: Int = 0) : DurationBasedAnimationSpec<T> {
    override fun <V : AnimationVector> vectorize(
        converter: TwoWayConverter<T, V>
    ): VectorizedDurationBasedAnimationSpec<V> = VectorizedSnapSpec(delay)
}

SnapSpec implementa la interfaz DurationBasedAnimationSpec, y TweenSpec que se presentó anteriormente también implementa la interfaz DurationBasedAnimationSpec, además de KeyframesSpec.

@Immutable
class TweenSpec<T>(
    val durationMillis: Int = DefaultDurationMillis,
    val delay: Int = 0,
    val easing: Easing = FastOutSlowInEasing
) : DurationBasedAnimationSpec<T> {

    override fun <V : AnimationVector> vectorize(converter: TwoWayConverter<T, V>) =
        VectorizedTweenSpec<V>(durationMillis, delay, easing)
}
interface DurationBasedAnimationSpec<T> : FiniteAnimationSpec<T> {
    override fun <V : AnimationVector> vectorize(converter: TwoWayConverter<T, V>):
        VectorizedDurationBasedAnimationSpec<V>
}

DurationBasedAnimationSpec, que describe AnimationSpecs en función de duraciones fijas, como KeyframesSpec, TweenSpec y SnapSpec. Estas AnimationSpecs basadas en la duración se pueden ejecutar repetidamente cuando se colocan en una RepeatableSpec.

Cambiamos el caso anterior para usar snap para observar el efecto de SnapSpec, y retrasamos la animación por 1 segundo para observar el efecto de snap jump.

@Composable
fun SnapDemo() {
    var small by remember {
        mutableStateOf(true)
    }
    val size: Dp by animateDpAsState(
        targetValue = if (small) 40.dp else 100.dp,
        animationSpec = snap(
            delayMillis = 1000
        )
    )
    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(onClick = { small = !small }) {
            Text(text = "改变方块大小-snap")
        }
        Box(
            modifier = Modifier
                .size(size)
                .background(Color.LightGray)
        )
    }
}

Especificaciones de fotogramas clave

KeyframesSpec para animar en función de los valores definidos por diferentes marcas de tiempo (es decir, diferentes fotogramas clave) durante la duración de la animación. Cada fotograma clave se puede definir mediante KeyframesSpecConfig.at.

@Stable
fun <T> keyframes(
    init: KeyframesSpec.KeyframesSpecConfig<T>.() -> Unit
): KeyframesSpec<T> {
    return KeyframesSpec(KeyframesSpec.KeyframesSpecConfig<T>().apply(init))
}

El método keyframes() oficial se usa para construir KeyframesSpec, con solo un parámetro KeyframesSpec.KeyframesSpecConfig.

    class KeyframesSpecConfig<T> {
        //动画持续时间,默认为300
        var durationMillis: Int = DefaultDurationMillis

        //动画延迟时间,默认为0
        var delayMillis: Int = 0

        //关键帧
        internal val keyframes = mutableMapOf<Int, KeyframeEntity<T>>()

        //添加一个关键帧在某个时间点时刻
        infix fun T.at(/*@IntRange(from = 0)*/ timeStamp: Int): KeyframeEntity<T> {
            return KeyframeEntity(this).also {
                keyframes[timeStamp] = it
            }
        }

        //添加一个关键帧在某个进度时刻
        infix fun T.atFraction(fraction: Float): KeyframeEntity<T> {
            return at((durationMillis * fraction).roundToInt())
        }

        //为刚提供的时间戳开始的时间间隔添加 Easing
        infix fun KeyframeEntity<T>.with(easing: Easing) {
            this.easing = easing
        }

KeyframesSpecConfig almacena la configuración variable de los fotogramas clave, incluidos la duraciónMillis, delayMillis y todos los fotogramas clave. Cada fotograma clave define un valor de animación en un momento específico.
Eche un vistazo al uso y efecto específico:

@Composable
fun KeyframesDemo() {
    var small by remember {
        mutableStateOf(true)
    }
    val size: Dp by animateDpAsState(
        targetValue = if (small) 40.dp else 100.dp,
        animationSpec = keyframes {
            durationMillis = 1000
            50.dp at 0 with LinearOutSlowInEasing
            60.dp at 100 with FastOutLinearInEasing
            70.dp at 300
            80.dp at 600
        }
    )
    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(onClick = { small = !small }) {
            Text(text = "改变方块大小-keyframes")
        }
        Box(
            modifier = Modifier
                .size(size)
                .background(Color.LightGray)
        )
    }
}

Especificaciones repetibles

RepeatableSpec, crea una animación repetible basada en DurationBasedAnimationSpec.

@Stable
fun <T> repeatable(
    iterations: Int,
    animation: DurationBasedAnimationSpec<T>,
    repeatMode: RepeatMode = RepeatMode.Restart,
    initialStartOffset: StartOffset = StartOffset(0)
): RepeatableSpec<T> =
    RepeatableSpec(iterations, animation, repeatMode, initialStartOffset)

El método repeatable() se proporciona oficialmente para construir un RepeatableSpec, con cuatro parámetros, se deben pasar al menos dos parámetros.

  • iteraciones: el número de repeticiones, teóricamente debería ser mayor que 1, igual a 1 significa que no hay repetición, por lo que no hay necesidad de usar RepeatableSpec.
  • animation: la AnimationSpec que se va a repetir debe ser DurationBasedAnimationSpec, es decir, se pueden utilizar KeyframesSpec, SnapSpec y TweenSpec.
  • modo de repetición: modo de repetición
  • initialStartOffset: el desplazamiento en el que comienza la animación

RepeatMode tiene dos modos:

enum class RepeatMode {
    //将重新启动动画,并从开始值到结束值进行动画处理。
    Restart,

    //将在动画重复时反转上一次迭代
    Reverse
}

Al repetir en modo RepeatMode.Reverse, se recomienda enfáticamente un número impar de iteraciones. De lo contrario, la animación puede saltar al valor final al finalizar la última iteración.

initialStartOffset se puede usar para retrasar el inicio de una animación o adelantarla a un tiempo de reproducción determinado. Este desplazamiento de inicio no se repetirá, pero sí lo hará el retraso (si lo hay) en la animación. Por defecto, el desplazamiento es 0.

@kotlin.jvm.JvmInline
value class StartOffset private constructor(internal val value: Long) {
   
    constructor(offsetMillis: Int, offsetType: StartOffsetType = StartOffsetType.Delay) : this(
        (offsetMillis * offsetType.value).toLong()
    )

    val offsetMillis: Int
        get() = abs(this.value.toInt())

    val offsetType: StartOffsetType
        get() = when (this.value > 0) {
            true -> StartOffsetType.FastForward
            false -> StartOffsetType.Delay
        }
}

StartOffset almacena un tiempo offsetMillis y un tipo StartOffsetType Hay dos tipos de tipos StartOffsetType: StartOffsetType.Delay retrasa el inicio de la animación y StartOffsetType.FastForward avanza rápidamente la animación a un tiempo de reproducción determinado y comienza a reproducirse inmediatamente.

@kotlin.jvm.JvmInline
value class StartOffsetType private constructor(internal val value: Int) {
    companion object {
        //延迟动画的开始
        val Delay = StartOffsetType(-1)

        //快进动画到给定的播放时间,并立即开始播放。
        val FastForward = StartOffsetType(1)
    }
}

Use repetible, establezca la animación en una animación de interpolación que cambie a una velocidad constante de 1 segundo, repita 3 veces, el método de repetición es repetición inversa, el initialStartOffset se establece en un retraso de 500 milisegundos y StartOffsetType.FastForward.

@Composable
fun RepeatableDemo() {
    var small by remember {
        mutableStateOf(true)
    }
    val size: Dp by animateDpAsState(
        targetValue = if (small) 40.dp else 100.dp,
        animationSpec = repeatable(
            iterations = 3,
            animation = tween(durationMillis = 1000, easing = LinearEasing),
            repeatMode = RepeatMode.Reverse,
            initialStartOffset = StartOffset(500, StartOffsetType.FastForward)
        )
    )
    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(onClick = { small = !small }) {
            Text(text = "改变方块大小-repeatable")
        }
        Box(
            modifier = Modifier
                .size(size)
                .background(Color.LightGray)
        )
    }
}

El efecto de animación completo: avance rápido a 500 milisegundos, luego comience la animación a una velocidad constante inmediatamente y luego comience la animación inversa 500 milisegundos más tarde.

InfiniteRepetableSpec

InfiniteRepeatableSpec, crea una animación que se repite infinitamente basada en DurationBasedAnimationSpec.

@Stable
fun <T> infiniteRepeatable(
    animation: DurationBasedAnimationSpec<T>,
    repeatMode: RepeatMode = RepeatMode.Restart,
    initialStartOffset: StartOffset = StartOffset(0)
): InfiniteRepeatableSpec<T> =
    InfiniteRepeatableSpec(animation, repeatMode, initialStartOffset)

El funcionario proporciona el método infiniteRepeatable() para construir InfiniteRepeatableSpec. En comparación con RepeatableSpec, hay una iteración de parámetro menos. Naturalmente, la animación de repetición infinita no necesita la cantidad de repeticiones, y el resto de los parámetros son los mismos.

@Composable
fun InfiniteRepeatableDemo() {
    var small by remember {
        mutableStateOf(true)
    }
    val size: Dp by animateDpAsState(
        targetValue = if (small) 40.dp else 100.dp,
        animationSpec = infiniteRepeatable(
            animation = tween(durationMillis = 1000, easing = LinearEasing),
            repeatMode = RepeatMode.Reverse,
            initialStartOffset = StartOffset(500, StartOffsetType.FastForward)
        )
    )
    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(onClick = { small = !small }) {
            Text(text = "改变方块大小-infiniteRepeatable")
        }
        Box(
            modifier = Modifier
                .size(size)
                .background(Color.LightGray)
        )
    }
}

FloatAnimationSpec

FloatAnimationSpec es una interfaz con dos clases de implementación: FloatTweenSpec solo realiza la animación TweenSpec para el tipo Float y FloatSpringSpec solo realiza la animación SpringSpec para el tipo Float. El oficial no proporciona un método que se pueda usar directamente, porque tween() y spring() admiten tipos de datos completos, y FloatAnimationSpec solo se usa cuando la capa subyacente realiza cálculos más detallados.

Supongo que te gusta

Origin blog.csdn.net/yuantian_shenhai/article/details/128164904
Recomendado
Clasificación