Transições de Estado

O sistema de transição do Vue oferece muitas maneiras simples de animar entradas, saídas e listas, mas e quanto à animação de seus próprios dados? Por exemplo:

Todos estes já estão armazenados como números brutos ou podem ser convertidos em números. Uma vez que fazemos isso, nós podemos animar estas mudanças de estado usando bibliotecas de terceiros, em combinação com os sistemas de reatividade e componente do Vue.

Animando Estado com Observadores

Observadores nos permitem animar mudanças de qualquer propriedade numérica para outra propriedade. Isso pode parecer complicado no começo, por isso vamos mergulhar em um exemplo usando GreenSock:

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>

<div id="animated-number-demo">
<input v-model.number="number" type="number" step="20">
<p>{{ animatedNumber }}</p>
</div>
new Vue({
el: '#animated-number-demo',
data: {
number: 0,
tweenedNumber: 0
},
computed: {
animatedNumber: function() {
return this.tweenedNumber.toFixed(0);
}
},
watch: {
number: function(newValue) {
TweenLite.to(this.$data, 0.5, { tweenedNumber: newValue });
}
}
})

{{ animatedNumber }}

Quando você atualiza o número, a mudança é animada abaixo do input. Isto produz uma demonstração agradável, mas o que acontece com o que não é diretamente gravado como um número, como qualquer cor CSS válida por exemplo? Veja como podemos conseguir isso com Tween.js e Color.js:

<script src="https://cdn.jsdelivr.net/npm/tween.js@16.3.4"></script>
<script src="https://cdn.jsdelivr.net/npm/color-js@1.0.3"></script>

<div id="example-7">
<input
v-model="colorQuery"
v-on:keyup.enter="updateColor"
placeholder="Informe uma cor"
>
<button v-on:click="updateColor">Atualizar</button>
<p>Visualização:</p>
<span
v-bind:style="{ backgroundColor: tweenedCSSColor }"
class="example-7-color-preview"
></span>
<p>{{ tweenedCSSColor }}</p>
</div>
var Color = net.brehaut.Color

new Vue({
el: '#example-7',
data: {
colorQuery: '',
color: {
red: 0,
green: 0,
blue: 0,
alpha: 1
},
tweenedColor: {}
},
created: function () {
this.tweenedColor = Object.assign({}, this.color)
},
watch: {
color: function () {
function animate () {
if (TWEEN.update()) {
requestAnimationFrame(animate)
}
}

new TWEEN.Tween(this.tweenedColor)
.to(this.color, 750)
.start()

animate()
}
},
computed: {
tweenedCSSColor: function () {
return new Color({
red: this.tweenedColor.red,
green: this.tweenedColor.green,
blue: this.tweenedColor.blue,
alpha: this.tweenedColor.alpha
}).toCSS()
}
},
methods: {
updateColor: function () {
this.color = new Color(this.colorQuery).toRGB()
this.colorQuery = ''
}
}
})
.example-7-color-preview {
display: inline-block;
width: 50px;
height: 50px;
}

Visualização:

{{ tweenedCSSColor }}

Transições de Estado Dinâmicas

Tal como acontece com os componentes de transição do Vue, as transições de estado de suporte de dados podem ser atualizadas em tempo real, o que é especialmente útil para prototipação! Mesmo usando um polígono SVG simples, você pode obter muitos efeitos que seriam difíceis de imaginar até que você tenha brincado um pouco com as variáveis.

Veja este fiddle para o código completo por detrás do exemplo acima.

Organizando Transições em Componentes

Gerenciar muitas transições de estado pode aumentar rapidamente a complexidade de uma instância ou componente Vue. Por sorte, muitas animações podem ser extraídas para componentes filho separados. Vamos fazer isso com o inteiro animado de nosso exemplo anterior:

<script src="https://cdn.jsdelivr.net/npm/tween.js@16.3.4"></script>

<div id="example-8">
<input v-model.number="firstNumber" type="number" step="20"> +
<input v-model.number="secondNumber" type="number" step="20"> =
{{ result }}
<p>
<animated-integer v-bind:value="firstNumber"></animated-integer> +
<animated-integer v-bind:value="secondNumber"></animated-integer> =
<animated-integer v-bind:value="result"></animated-integer>
</p>
</div>
// Esta lógica tweening complexa agora pode ser reusada por
// quaisquer inteiros que desejemos animar em nossa aplicação.
// Componentes também oferecem uma interface clara para configurar
// transições mais dinâmicas e estratégias de transição mais
// complexas.
Vue.component('animated-integer', {
template: '<span>{{ tweeningValue }}</span>',
props: {
value: {
type: Number,
required: true
}
},
data: function () {
return {
tweeningValue: 0
}
},
watch: {
value: function (newValue, oldValue) {
this.tween(oldValue, newValue)
}
},
mounted: function () {
this.tween(0, this.value)
},
methods: {
tween: function (startValue, endValue) {
var vm = this
function animate () {
if (TWEEN.update()) {
requestAnimationFrame(animate)
}
}

new TWEEN.Tween({ tweeningValue: startValue })
.to({ tweeningValue: endValue }, 500)
.onUpdate(function (object) {
vm.tweeningValue = object.tweeningValue.toFixed(0)
})
.start()

animate()
}
}
})

// Toda a complexidade foi agora removida da instância principal do Vue!
new Vue({
el: '#example-8',
data: {
firstNumber: 20,
secondNumber: 40
},
computed: {
result: function () {
return this.firstNumber + this.secondNumber
}
}
})
+ = {{ result }}

+ =

Dentro de componentes filho nós podemos usar qualquer combinação das estratégias de transição que foram apresentadas nesta página, juntamente com aquelas oferecidas pelo sistema de transição integrado do Vue. Juntos, há poucos limites para o que pode ser realizado.

Trazendo Desenhos à Vida

Animar, por definição, significa trazer à vida. Infelizmente, quando designers criam ícones, logos e mascotes, eles geralmente os entregam como imagens ou SVGs estáticos. Embora o gato-polvo do GitHub, o pássaro do Twitter e muitos outros logos se assemelhem a criaturas vivas, eles não parecem estar realmente vivos.

Vue pode ajudar. Como SVGs são apenas dados, nós só precisamos de exemplos do que essas criaturas se parecem quando estão animadas, pensativas ou alertas. Então Vue pode ajudar na transição entre estes estados, tornando suas páginas de bem-vindo, indicadores de carregamento e notificações mais emocionalmente atraentes.

Sarah Drasner demonstra isso na animação abaixo, usando uma combinação de mudanças de estado de tempo e interatividade.

Veja o Pen Vue-controlled Wall-E de Sarah Drasner (@sdras) no CodePen.