Transiciones de estado

El sistema de transición de Vue ofrece muchas formas sencillas de animar la entrada, salida y listas, pero ¿qué hay de animar sus propios datos? Por ejemplo:

Todos estos ya están almacenados como números sin procesar o se pueden convertir en números. Una vez que hagamos eso, podemos animar estos cambios de estado utilizando librerías de terceros para interpolar el estado, en combinación con la reactividad y los sistemas de componentes de Vue.

Animando el estado con los Watchers

Los Watchers nos permiten animar los cambios de cualquier propiedad numérica en otra propiedad. Esto puede parecer complicado en el resumen, así que vamos a ver un ejemplo 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 }}

Cuando actualiza el número, el cambio se anima debajo de la entrada. Esto lo convierte en una buena demostración, pero ¿qué pasa con algo que no se almacena directamente como un número, como cualquier color CSS válido, por ejemplo? Así es como podríamos lograr esto con Tween.js y 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="Ingrese un color"
>
<button v-on:click="updateColor">Actualizar</button>
<p>Vista previa:</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;
}

Vista previa:

{{ tweenedCSSColor }}

Transiciones de estado dinámico

Al igual que con los componentes de transición de Vue, las transiciones de estado de respaldo de datos se pueden actualizar en tiempo real, lo que es especialmente útil para la creación de prototipos. Incluso utilizando un simple polígono SVG, puedes lograr muchos efectos que serían difíciles de concebir hasta que hayas jugado un poco con las variables.

Vea este fiddle para el código completo detrás de la demostración anterior.

Organizando transiciones en componentes

La gestión de muchas transiciones de estado puede aumentar rápidamente la complejidad de una instancia o componente de Vue. Afortunadamente, muchas animaciones se pueden extraer en componentes secundarios dedicados. Hagamos esto con el número entero animado de nuestro ejemplo 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 compleja lógica de interpolación ahora puede reutilizarse entre
// cualquier número entero que deseemos animar en nuestra aplicación.
// Los componentes también ofrecen una interfaz limpia para configurar
// transiciones más dinámicas y estrategias de transición complejas.
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 la complejidad se ha eliminado de la instancia principal de Vue!
new Vue({
el: '#example-8',
data: {
firstNumber: 20,
secondNumber: 40
},
computed: {
result: function () {
return this.firstNumber + this.secondNumber
}
}
})
+ = {{ result }}

+ =

Dentro de los componentes hijos, podemos usar cualquier combinación de estrategias de transición que se hayan cubierto en esta página, junto con las ofrecidas por el sistema de transición incorporado de Vue. Juntos, hay muy pocos límites a lo que se puede lograr.

Trayendo los diseños a la vida

Animar, según una definición, significa dar vida. Desafortunadamente, cuando los diseñadores crean iconos, logotipos y mascotas, por lo general se entregan como imágenes o SVG estáticos. Entonces, aunque el octocat de GitHub, el pájaro de Twitter y muchos otros logotipos se parecen a criaturas vivientes, en realidad no parecen estar vivos.

Vue puede ayudar. Dado que los SVG son solo datos, solo necesitamos ejemplos de cómo se ven estas criaturas cuando están excitados, pensando o alarmados. Entonces Vue puede ayudar a la transición entre estos estados, haciendo que sus páginas de bienvenida, indicadores de carga y notificaciones sean más convincentes desde el punto de vista emocional.

Sarah Drasner demuestra esto en la siguiente demostración, utilizando una combinación de cambios de estado programados e interactivos:

Vea Wall-E controlado-por-Vue por Sarah Drasner (@sdras) en CodePen.