Original Article: Animator.js: JavaScript animation library
Author: Bernie

Animator.js: Biblioteca de animação JavaScript

Ver animator.js no GitHub

Animator.js foi uma biblioteca que escrevi em 2006 para lidar com animação em páginas da web. Por um tempo, estava bastante à frente da curva: era a primeira biblioteca a caracterizar o morphing CSS - a capacidade de transição suave entre dois estilos definidos como classes CSS.

O mundo do JavaScript mudou muito desde então. Animator.js agora foi incorporado na maioria das principais estruturas JavaScript, seja por portar diretamente o código (Animator.js é lançado sob uma licença BSD que permite que as pessoas façam isso) ou por emprestando as técnicas. Em particular, as animações CSS produzem melhores resultados e usam menos energia do processador. Para uma biblioteca moderna que expõe animações CSS através de uma API semelhante, recomendo jQuery.Transit.

Estou mantendo esta página aqui para interesse histórico, porque está escrito em um estilo tutorial que será apropriado se você quiser aprender a criar animação programática em qualquer idioma.

Graças a Tim Stone, Kaspar Fischer, Clint Priest e outros desenvolvedores que contribuíram com comentários e recursos.

Antecedentes e motivação

Eu removi esta seção porque continha críticas de outras bibliotecas de JavaScript que eram verdadeiras em 2006, mas não hoje. Eu também aproveitei a oportunidade para falar sobre o tema da herança orientada a objetos, que foi um tópico que eu explorei em maior extensão no artigo A herança é má e deve ser destruída - Bernie Sumption, 2010

Uma maneira melhor de animar

Um Animador é um objeto que gera uma série de números entre zero e um. Zero representa o início da animação, um fim.

Veja isso:

// Este objeto controla o progresso da animação
ex1 = new Animator()
// Os sujeitos do Animador definem seu comportamento
ex1.addSubject(updateButton);
function updateButton(value) {
    var button = document.getElementById("ex1Button");
    button.innerHTML = "Progress: " + Math.round(value * 100) + "%";
}
// Clique agora abaixo: cada clique para o botão chama ex1.toggle()

O Animador acima foi deixado com as opções padrão, aqui está um exemplo que usa mais configuração:

ex2 = Novo Animador({
    duration: 1200,
    interval: 400,
    onComplete: function() {
        document.getElementById("ex2Target").innerHTML += "Bing! ";
    }
})
ex2.addSubject(updateButton);
function updateButton(value) {
    document.getElementById("ex2Target").innerHTML += " Badda ";
}

Animando estilos de elemento

Na maioria das vezes você quer animar uma ou mais propriedades de estilo de um elemento. Existem essencialmente apenas três tipos de valores CSS - aqueles que escalam numericamente (como 10px), Com um valor de cor RGB (como #RRGGBB), E aqueles que não escalam (como bold / italic). Animator Fornece três classes de utilidade para cada um desses tipos de propriedades, e entre eles podem animar qualquer estilo CSS.

// animate margin-left de 0 para 100 px
ex3 = new Animator().addSubject(
    new NumericalStyleSubject(
        "ex3Button",
        "margin-left",
        0,
        100));

// Animar cor de fundo de branco para preto
ex4 = new Animator().addSubject(
    new ColorStyleSubject(
        "ex4Button",
        "background-color",
        "#FFFFFF",
        "#F4C"));

// Animando ambos - observe como as chamadas para addSubject () podem ser encadeadas:
ex5 = new Animator()
    .addSubject(
        new NumericalStyleSubject(
            "ex5Button",
            "margin-left",
            0,
            100))
    .addSubject(
        new ColorStyleSubject(
            "ex5Button",
            "background-color",
            "#FFFFFF",
            "#F4C"))
    .addSubject(
        new DiscreteStyleSubject(
            "ex5Button", "font-weight",
            "normal",
            "bold",
            0.5));
// Também, verifique o último assunto, o que faz com que font-weight
//Mudar de normal para negrito meio caminho através da animação

Se você já usou moofx ou scriptaculous, então você provavelmente está pensando que isso é bastante detalhado, e você estaria certo. Animator Tem um recurso assassino que remove a verbosidade, mas antes de chegarmos a isso, aqui estão mais algumas coisas que você pode fazer:

Efeitos mais complexos

E se você tiver um número de elementos que você deseja animar da mesma maneira? Nesse caso, passe um conjunto de elementos Subject's construtor. Não é possível adicionar e remover elementos de um Subject Depois de ter sido construído - se você quiser fazer isso, use um Subject Para cada elemento e usar addSubject() e removeSubject() no Animator.

// Aplicar o mesmo efeito a diferentes elementos é fácil
ex6 = new Animator().addSubject(
    new NumericalStyleSubject(
        ["dot1", "dot2", "dot3"],
        "margin-right",
        10,
        50));
     

Em exemplos anteriores, cada sujeito referiu-se ao mesmo elemento. Isso não tem que ser o caso:

// Aplicar diferentes efeitos a diferentes elementos é possível
ex7 = new Animator()
    .addSubject(
        new ColorStyleSubject(
            "ex5ButtonA",
            "background-color",
            "#FF9",
            "#9F9"))
    .addSubject(
        new NumericalStyleSubject(
            "ex5ButtonB",
            "padding",
            "5px",
            "15px"));

   

Opacidade recebe tratamento especial. Uma vez que o IE não suporta o estilo CSS standard 'opacity', NumericalStyleSubject Irá convertê-lo em um filtro apropriado.

ex8 = new Animator().addSubject(
    new NumericalStyleSubject(
        "ex8Button",
        "opacity",
        1,
        0.25));

Controlando a animação

Quando você clica em um botão de amostra neste artigo, está chamando toggle () em um objeto animador. Há mais algumas funções de controle:

  • play() Reproduz a animação do início ao fim
  • stop() Pára a animação em sua posição atual
  • reverse() Reproduz a animação em sentido inverso
  • seekTo(pos) Reproduz a animação a partir da sua posição atual
  • seekdeTo(de, to)
play()
0%
stop()
reverse()
seekTo(0.5)
seekdeTo(0.25, 0.75)

O benefício de usar seekTo () é que ele vai evitar saltos súbitos no estado quando chamado meio caminho através de uma animação:

Esta div usa play () e reverse em mouseover e mouseout
Esta div usa seekTo(1) e seekTo(0) em mouseover e mouseout

Transições

Uma transição é uma função que toma um estado (um número entre 0 e 1) e retorna outro número entre 0 e 1. Isso pode ser usado para simular aceleração, desaceleração e efeitos mais complexos. Você pode passar em uma transição para um Animator Construtor do objeto usando o nome inteligente propriedade transition.

O objeto Animator.tx fornece algumas transições feitas:
  • Animator.tx.easeInOut É a transição padrão, e cria um efeito suave
  • Animator.tx.linear Mantém uma taxa de animação constante
  • Animator.tx.easeIn Começa lento, fica mais rápido
  • Animator.tx.strongEaseIn Versão exagerada do acima
  • Animator.tx.easeOut Começa rápido, fica mais lento
  • Animator.tx.strongEaseOut Versão exagerada do acima
  • Animator.tx.elastic Vá um pouco além do ponto de destino e, em seguida,
  • Animator.tx.veryElastic Como acima, mas com uma passagem extra
  • Animator.tx.bouncy Acertar o ponto de destino, em seguida, voltar
  • Animator.tx.veryBouncy Como acima, mas com um extra de 2 rejeições

Às vezes você vai querer afinar as transições acima. Se você olhar para o código-fonte onde o Animator.tx Objeto é criado, você verá que as funções acima são todas feitas por quatro funções de fábrica:

Animator.makeEaseIn()

// Fazer uma transição que gradualmente acelera. Passe em 1 para suave
// Aceleração gravitacional, maiores valores para um efeito exagerado
ex9 = new Animator({
    transition: Animator.makeEaseIn(3),
    duration: 1000
});
ex9.addSubject(
    new NumericalStyleSubject(
        "ex9Button",
        "margin-left",
        0,
        200));

Animator.makeElastic()

// Fazer uma transição que, como um objeto com momentum sendo
// Atraído para um ponto alvo, ultrapassa o alvo e retorna
ex10 = new Animator({
    transition: Animator.makeElastic(3),
    duration: 2000
});
ex10.addSubject(
    new NumericalStyleSubject(
        "ex10Button",
        "margin-left",
        0,
        200));

Animator.makeBounce()

// Fazer uma transição que, como uma bola caindo ao chão,
// O alvo e salta de volta
ex11 = new Animator({
    transition: Animator.makeBounce(3),
    duration: 2000
});
ex11.addSubject(
    new NumericalStyleSubject(
        "ex11Button",
        "margin-left",
        0,
        200));

Animator.makeADSR()

Um ataque Decay Sustain Release envelope é uma técnica que eu tirei da produção musical. É muito útil para animações que começam e terminam com o mesmo valor.

// Este exemplo mostra o que um envelope ADSR parece, mas é
// Caso contrário, os três primeiros argumentos são a fronteira
// Pontos das 4 fases. O último é o nível de sustentação.
// Todos devem estar entre 0 e 1.
ex12 = new Animator({
    transition: Animator.makeADSR(0.25, 0.5, 0.75, 0.5),
    duration: 2000
});
ex12.addSubject(
    new NumericalStyleSubject(
        "ex12Button",
        'margin-left',
        0,
        400));

Um uso prático de ADSR é realizar uma animação em um determinado estado por um tempo, como neste exemplo amarelo fade

// Este desvanecimento amarelo é enfatizado segurando-o em amarelo
// para a primeira metade da animação
ex13= new Animator({
    transition: Animator.makeADSR(0, 0, 0.5, 1),
    duration: 1500
});
ex13.addSubject(
    new ColorStyleSubject(
        "ex13Button",
        "background-color",
        "#FFFFFF",
        "#FFFF00"));

Funções personalizadas

Claro, você pode escrever suas próprias funções que fazem qualquer tipo de transição:

function setupEx14() {
    var wobbles = parseInt(prompt(
        "Enter a number of wobbles (try between 1 and 5)", ""));
    if (!wobbles) {
        alert("Sorry, I didn't understand that, have 2 wobbles.");
        wobbles = 2;
    }
    // Fazer algum tipo de material trigonométrico louco. Eu nem sequer
    // Sei o que isso significa, eu só fiquei louco com a Matemática
    // functions
    ex14Tx = function(pos) {
        return ((-Math.cos(pos*Math.PI*((1+(2*wobbles))*pos))/2) + 0.5);
    }
    ex14 = new Animator({
        transition: ex14Tx,
        duration: 2000
    });
    ex14.addSubject(
        new NumericalStyleSubject(
            "ex14Button",
            "margin-left",
            0,
            100));
    ex14.play();
};

 

O recurso assassino

Eu queria explicar como o Animator trabalha sob o capô antes de revelar esse recurso.

Como eu disse antes, é tudo um pouco detalhado no momento, ea maioria do código nos exemplos acima é apenas boilerplate. O que precisamos é de algum tipo de linguagem que nos permita definir o estilo que queremos Animar um objeto em direção. Oh espere um segundo, nós já temos um: CSS. Um estilo CSS contém todas as informações necessárias para definir um estado de animação:

ex15 = new Animator().addSubject(
    new CSSStyleSubject(
        "ex15Button",
        "width: 12em; background-color: rgb(256, 256, 256); font-style: normal",
        "width: 40em; background-color: #F39; font-style: italic"));
// Note como você pode usar qualquer unidade, não apenas 'px'.

CSSStyleSubject É um invólucro em torno dos outros três Subjects. Analisa dois conjuntos de regras CSS e, para cada declaração de propriedade, cria um NumericalStyleSubject Se ele se parece com um número, ou ColorStyleSubject Se parece com uma cor, ou DiscreteStyleSubject de outra forma. DiscreteStyleSubjects São criados com um limiar de 0,5, em outras palavras o estilo muda de normal para itálico meio caminho através da animação.

Convenientemente, você também pode passar em nomes de classe CSS em vez de conjuntos de regras:

ex16 = new Animator().addSubject(new CSSStyleSubject(
    "ex16Button",
    "small white",
    "big blue bendy"));
// As classes pequenas, grandes, brancas, azuis e curvas são
// Definido na fonte desta página.

Quando você está criando animações de classes CSS, é fácil perder a noção do que o objeto animador está fazendo. o Animator.inspect() Retorna uma string que descreve o animador:

Isso é muito bom, mas ainda podemos remover alguns mais cruft. Na maioria das vezes, o elemento que você deseja animar já estará no seu estilo inicial. Se este for o caso, você pode omitir um dos conjuntos de regras eo estado inicial será inferido a partir do estilo atual elementos. Isso getComputedStyle (ou Element.currentStyle No IE) assim reflete o estilo real do elemento depois de aplicar as regras CSS em folhas de estilo.

ex17 = new Animator().addSubject(
    new CSSStyleSubject(
        "ex17Button",
        "width: 300px; background-color: #F39"));

Finalmente, há um último bit de açúcar sintático para tornar mais fácil a aplicação de efeitos. o Animator.apply(element, style, options) É um wrapper em torno de criar um único CSSStyleSubject. O segundo argumento é o estilo a desaparecer e o terceiro é um conjunto opcional de parâmetros de construtor para o Animator objeto. Se você quiser especificar o completo de e para estilos, passe em uma matriz de dois itens como o segundo parâmetro.

ex18 = Animator.apply("ex18Button", "greenish"); // ta da!

Oh vá em, mais uma característica...

Por demanda popular ... Várias pessoas pediram uma maneira fácil de encadear várias animações juntos. AnimatorChain É um objeto que se comporta um pouco como um Animator, Mas envolve vários outros Animator Objetos e faz com que eles toquem em seqüência.

var animators = [];
for (var i=0; i<3; i++) {
    animators[i] = Animator.apply("ex18blob-" + i), "blobEnd";
}
ex19 = new AnimatorChain(animators);
// O objeto AnimatorChain tem toggle(), play(), reverse()
// e seekTo(state) Funções como objetos Animator,
// Assim você pode usá-los freqüentemente onde o código espera um Animator

Bem, é isso. Aproveite o uso do Animator.js.