Personalizar animações

Geralmente, muitas das APIs de animação aceitam parâmetros para personalização de comportamentos.

Personalizar animações com o parâmetro AnimationSpec

A maioria das APIs de animação permite que os desenvolvedores personalizem as especificações de animação usando um parâmetro AnimationSpec opcional.

val alpha: Float by animateFloatAsState(
    targetValue = if (enabled) 1f else 0.5f,
    // Configure the animation duration and easing.
    animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing)
)

Existem diferentes tipos de AnimationSpec para criação de diferentes tipos de animação.

Criar animação baseada em física usando spring

A spring cria uma animação baseada em física entre os valores inicial e final. Ela precisa de dois parâmetros: dampingRatio e stiffness.

dampingRatio define o grau de mobilidade da spring. O valor padrão é Spring.DampingRatioNoBouncy.

Figura 1. Definindo proporções de amortecimento da mola diferentes.

stiffness define a velocidade de movimento da spring em direção ao valor final. O valor padrão é Spring.StiffnessMedium.

Figura 2. Como definir uma rigidez da mola diferente

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioHighBouncy,
        stiffness = Spring.StiffnessMedium
    )
)

A spring pode processar interrupções de forma melhor que tipos de AnimationSpec baseados em duração, porque garante a continuidade da velocidade quando o valor de destino muda entre animações. spring é usada como o AnimationSpec padrão por muitas APIs de animação, como animate*AsState e updateTransition.

Por exemplo, se aplicarmos uma configuração spring à animação a seguir, que é acionada pelo toque do usuário, ao interromper a animação à medida que ela avança, o uso de tween não responde tão bem quanto usar spring.

Figura 3. Definir as especificações tween vs. spring para animação e interrompê-la.

Animar entre os valores inicial e final usando uma curva de easing com tween

tween executa animações entre os valores inicial e final na durationMillis especificada, usando uma curva de easing. tween é uma abreviação da palavra "entre", porque vai entre dois valores.

Você também pode especificar delayMillis para adiar o início da animação.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = tween(
        durationMillis = 300,
        delayMillis = 50,
        easing = LinearOutSlowInEasing
    )
)

Consulte Easing para mais informações.

Animar a valores específicos em determinados momentos com keyframes

keyframes executam animações com base nos valores resumidos especificados em carimbos de data/hora diferentes na duração da animação. O valor de animação será interpolado entre dois valores keyframe a qualquer momento. O easing pode ser especificado para qualquer um dos keyframes para determinar a curva de interpolação.

Especificar os valores como 0 ms e o tempo de duração é opcional. Caso esses valores não sejam especificados, o padrão será os valores inicial e final da animação, respectivamente.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = keyframes {
        durationMillis = 375
        0.0f at 0 with LinearOutSlowInEasing // for 0-15 ms
        0.2f at 15 with FastOutLinearInEasing // for 15-75 ms
        0.4f at 75 // ms
        0.4f at 225 // ms
    }
)

Repetir uma animação com repeatable

repeatable executa uma animação baseada em duração (como tween ou keyframes) repetidamente, até alcançar a contagem de iteração especificada. É possível transmitir o parâmetro repeatMode para especificar se a animação vai ser repetida começando do início (RepeatMode.Restart) ou do final (RepeatMode.Reverse).

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = repeatable(
        iterations = 3,
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    )
)

Repetir uma animação infinitamente com infiniteRepeatable

infiniteRepeatable é semelhante a repeatable, mas essa função se repete por uma quantidade infinita de iterações.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = infiniteRepeatable(
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    )
)

Animações que usam infiniteRepeatable não são executadas em testes que usam ComposeTestRule. O componente será renderizado usando o valor inicial de cada valor de animação.

Ajustar imediatamente o valor final com snap

snap é uma AnimationSpec especial que muda imediatamente o valor para o valor final. Você pode especificar delayMillis para atrasar o início da animação.

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = snap(delayMillis = 50)
)

Definir uma função de easing personalizada

As operações AnimationSpec baseadas em duração (como tween ou keyframes) usam Easing para ajustar a fração da animação. Isso permite que o valor de animação acelere e desacelere, em vez de se mover a uma taxa constante. Uma fração é um valor entre 0 (início) e 1,0 (fim) indicando o ponto atual da animação.

O easing é, na verdade, uma função que usa um valor de fração entre 0 e 1,0 e retorna um ponto flutuante. O valor retornado pode estar fora do limite para representar uma ultrapassagem ou uma redução. Um easing personalizado pode ser criado, como no código abaixo.

val CustomEasing = Easing { fraction -> fraction * fraction }

@Composable
fun EasingUsage() {
    val value by animateFloatAsState(
        targetValue = 1f,
        animationSpec = tween(
            durationMillis = 300,
            easing = CustomEasing
        )
    )
    // ……
}

O Compose oferece várias funções Easing integradas que abrangem a maioria dos casos de uso. Consulte Velocidade: Material Design para saber mais informações sobre o easing usar dependendo do cenário.

  • FastOutSlowInEasing
  • LinearOutSlowInEasing
  • FastOutLinearEasing
  • LinearEasing
  • CubicBezierEasing
  • Veja mais

Animar tipos de dados personalizados convertendo e para AnimationVector

A maioria das APIs de animação do Compose oferece suporte a Float, Color, Dp e outros dados básicos. tipos como valores de animação por padrão, mas às vezes você precisa animar outros tipos de dados, incluindo os personalizados. Durante a animação, qualquer valor de animação é representado como um AnimationVector. O valor é convertido em um AnimationVector, e vice-versa, por um TwoWayConverter correspondente para que o sistema de animação principal possa processá-lo de maneira uniforme. Por exemplo, uma Int é representada como um AnimationVector1D contendo um único valor flutuante. Para TwoWayConverter, o Int é assim:

val IntToVector: TwoWayConverter<Int, AnimationVector1D> =
    TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })

A Color é essencialmente um conjunto de quatro valores: vermelho, verde, azul e alfa. A Color é convertida em um AnimationVector4D contendo quatro valores flutuantes. Dessa forma, cada tipo de dados usado nas animações é convertido em AnimationVector1D, AnimationVector2D, AnimationVector3D ou AnimationVector4D, dependendo da dimensionalidade. Isso permite que diferentes componentes do objeto sejam animados de forma independente, com rastreamentos de velocidade próprios. É possível acessar conversores integrados para tipos de dados básicos usando conversores como Color.VectorConverter ou Dp.VectorConverter.

Caso queira adicionar compatibilidade com um novo tipo de dados como um valor de animação, crie seu próprio TwoWayConverter e forneça-o à API. Por exemplo, é possível usar animateValueAsState para animar o tipo de dados personalizado da seguinte forma:

data class MySize(val width: Dp, val height: Dp)

@Composable
fun MyAnimation(targetSize: MySize) {
    val animSize: MySize by animateValueAsState(
        targetSize,
        TwoWayConverter(
            convertToVector = { size: MySize ->
                // Extract a float value from each of the `Dp` fields.
                AnimationVector2D(size.width.value, size.height.value)
            },
            convertFromVector = { vector: AnimationVector2D ->
                MySize(vector.v1.dp, vector.v2.dp)
            }
        )
    )
}

A lista a seguir inclui algumas VectorConverters integradas: