Animação por tempo


A animação por tempo resulta de definir chaves (keys): Os valores de certos parâmetros em determinados instantes.

Por exemplo: A posição x = 1 no instante t = 0 e x = 2 no instante t = 2.


É necessário calcular automaticamente os valores intermédios dos parâmetros que controlam uma animação.

Por exemplo:

  • A posição de objeto é definida pelo parâmetro x.
  • É data a posição inicial: x = 1 quando t = 0.5.
  • E a posição final: x = 2 quando t = 2.5.
  • Sempre que necessário, pretende-se calcular as posições intermédias desse objeto.

Interpolação linear de valores intermédios entre duas chaves

Chaves Valores Interpolados
const keys = [
  {
    t: 0.0,
    parameters: {  x: 1.00  }
  },
  {
    t: 2.0,
    parameters: {  x: 2.00  }
  }
];

image

As chaves definem os pontos «vermelhos»; o início e o fim.

Os pontos «verdes» são calculados pela interpolação linear entre os pontos «vermelhos».

Processo da Animação por Tempo

  1. São definidas chaves (keys) específicas.
  • Cada chave define um instante e valores de parâmetros.
  1. Durante a animação:
  • Os valores dos parâmetros são recalculados sempre que necessário.
  • O modelo gráfico é atualizado com os novos valores dos parâmetros e construído/desenhado de novo.

A animação por tempo resolve o problema das diferenças de frequências que a animação por fotogramas coloca.

Sempre que o sistema gráfico está pronto para mostrar um novo fotograma, os valores dos parâmetros são recalculados; o modelo gráfico é atualizado com os novos valores dos parâmetros e desenhado de novo.

Isto significa que a definição de uma animação é feita através de modelo e esse modelo tem parâmetros que definem cada «fotograma» na sequência.

Modelos e Parâmetros


  • Modelo
    • Conjunto de variáveis (parâmetros) que definem cada «fotograma» na sequência.
  • Parâmetro
    • Variável que define um elemento da animação.

Exemplos de Modelos e Parâmetros

  1. Um quadrado vermelho desloca-se da esquerda para a direita. Neste caso o modelo necessita apenas de um parâmetro, a abcissa () do quadrado:

    const model = { x: 0 };
    
  2. Um quadrado vermelho desloca-se na horizontal e na vertical. O modelo necessita de dois parâmetros, a abcissa () e a ordenada () do quadrado:

    const model = { x: 0, y: 0 };
    
  3. Um quadrado desloca-se na horizontal, na vertical e pode mudar de cor:

    const model = { x: 0, y: 0, color: 'crimson' };
    
  4. Vários quadrados deslocam-se na horizontal, na vertical e podem mudar a cor:

    const model = { my_squares: [
      { x: 0, y: 0, color: 'crimson' },
      { x: 0, y: 8, color: 'khaki' }
    ] };
    

Tweens

Os tweens (contração do inglês «in-between») assentam uma técnica simples mas flexível e eficiente para:

  • calcular os valores intermédios entre
  • um valor inicial e
  • um valor final durante
  • um certo intervalo de tempo .

O valor intermédio, , quando , é calculado usando a fórmula:

Um tween atualiza, ao longo de um certo intervalo de tempo , um valor que varia de quanto para quando .

Os parâmetros fundamentais de um tween são:

  • Valor Inicial ()
    • Em que valor começa a variação do parâmetro.
  • Valor Final ()
    • Em que valor termina a variação do parâmetro.
  • Tempo Inicial ()
    • Em que instante começa a variação do parâmetro.
  • Tempo Final () ou Duração ()
    • Em que instante termina a variação (tempo final) ou quanto tempo demora a variação (duração). A relação entre o tempo inicial, o tempo final e a duração é:

Os tweens são usados para fazer animações com modelos parametrizados.

Tipos de Tweens

Os tweens descritos acima são lineares: a variação do parâmetro é proporcional ao tempo decorrido.

function draw(c, m) {
  c.fillStyle = "steelblue";
  c.fillRect(0, 0, 480, 120);
  c.fillStyle = "crimson";
  c.fillRect(m.linear.x, 10, 40, 40);
  c.fillStyle = "khaki";
  c.fillRect(m.nonlinear.x, 70, 40, 40);
}

const context = document
    .getElementById("anim:tween:1")
    .getContext("2d");

const model = {
  linear: {x:  10,},
  nonlinear: {x: 10}
};

const linear = new TWEEN.Tween(model.linear)
    .to({x: 430}, 2000)
    .easing(TWEEN.Easing.Linear.None)
    .yoyo(true)
    .repeat(Infinity)
    .start();

const cubic = new TWEEN.Tween(model.nonlinear)
    .to({x: 430}, 2000)
    .easing(TWEEN.Easing.Quadratic.InOut)
    .yoyo(true)
    .repeat(Infinity)
    .start();

const step = function(ctx, mdl) {
    TWEEN.update();
    draw(ctx, mdl);
    requestAnimationFrame(function () {
      step(ctx, mdl)
    });
  }

step(context, model);

Para animações com «objetos naturais» este tipo de tween não funciona bem porque produz movimentos uniformes, com velocidade constante.

  • O resultado é semelhante ao movimento dos robots nos filmes antigos, ou de segunda categoria e também de alguns estilos de dança.

Os movimentos naturais não são lineares. Alguns aceleram no início e travam no fim. Outros têm uma fase de «ganhar balanço». As variantes são muitas.

  • Easing define a aceleração de um tween e, em geral, tem as algumas variantes bem definidas.
  • Ease-In tipo de aceleração no início da variação.
  • Ease-Out tipo de aceleração no fim da variação.

Suporte para Tweens

A biblioteca tween.js proporciona um sistema flexível e eficiente para definir e usar tweens. Os tweens desta biblioteca:

  1. São construídos com um valor inicial, um valor final e uma duração.
  2. Aceitam uma grande variedade de easings.
  3. Podem ser operados de várias formas:
    • yoyo: depois de atingir o valor final, o tween volta ao inicial.
    • repetir: o tween é repetido um certo número de vezes.
    • encadear: aplicar um segundo tween após a conclusão do primeiro.
    • etc.

O Ciclo de Animação

Qualquer animação é um ciclo infinito de atualização (update) e construção (render) de um modelo.

Os ciclos de animação e o uso dos tweens têm essencialmente a mesma estrutura e podem ser facilmente unificados.

Ciclo de Animação Ciclo dos Tweens
const m = initial_model();
while (true) {
    m.update();
    m.render();
}
const t = TWEEN.Tween(...) ... ;
t.start();
while (true) {
    t.update();
    const x = t.value();
    ...
}

Programação Web e Ciclos Infinitos

Ciclos infinitos e requestAnimationFrame

No contexto da programação na Web, os ciclos infinitos devem ser implementados com a função requestAnimationFrame.

function draw(context, model) {
  /// Desenha o modelo atualizado no contexto fornecido.
}

function update(model) {
  // Por exemplo, usando TWEEN.update();
}

const model = {
  // valores iniciais dos parâmetros;
};

const step = function(ctx, mdl) {
    update(mdl);    // Atualiza os parâmetros do modelo.
    draw(ctx, mdl); // Desenha o modelo atualizado.
    requestAnimationFrame(function () { // Repete o ciclo de animação.
      step(ctx, mdl)
    });
  }

step(context, model); // Inicia o ciclo de animação.

A razão para fazer o ciclo de animação com a função requestAnimationFrame é que, desta forma, o navegador pode pausar a execução do código JavaScript quando necessário.

Na versão seguinte versão alternativa, o ciclo de animação é implementado usando um ciclo while infinito que bloqueia o navegador e portanto, deve ser evitado.

function draw(context, model) {
  /// Desenha o modelo atualizado no contexto fornecido.
}

function update(model) {
  // Por exemplo, usando TWEEN.update();
}

const model = {
  // valores iniciais dos parâmetros;
};

// BAD BAD NOT GOOD
while (true) {  // ** O NAVEGADOR FICA BLOQUEADO AQUI **
  update(model);        // Atualiza os parâmetros do modelo.
  draw(context, model); // Desenha o modelo atualizado.
}