Componente de Progreso de Actos en Vue.js
Clase 19 de 27 • Curso Avanzado de Vue.js 2
Uno de los valores que nos devuelve la API del juego cuando le pedimos los datos de un jugador es el progreso. Esto nos indica el estado de los actos (hay cinco), es decir, si los ha completado o no.
Vamos a crear un componente, muy sencillo, que nos indique si el usuario ha completado un acto o no. Para indicar si el acto está completado o no usaremos una imagen (Sprites CSS), que estará oscurecida y con un candado cuando esté sin completar y sin el candado y bien clara cuando esté completado.
La imagen que usaremos como Sprite CSS es esta:
Lo primero que vamos a hacer es crear los estilos CSS, en nuestro fichero global de CSS, /assets/main.styl
. Vamos a añadir el siguiente contenido:
// --------------------- // Acts // --------------------- .act background-image: url('../img/progress.png') background-size 500% &.act1 &.pending background-position -1px 0 &.done background-position -1px 57px &.act2 &.pending background-position -51px 0 &.done background-position -51px -56px &.act3 &.pending background-position -101px 0px &.done background-position -101px -168px &.act4 &.pending background-position -151px 0px &.done background-position -151px -168px &.act5 &.pending background-position 50px 0 &.done background-position 50px 57px
Hemos creado unas clases CSS que nos van a permitir mostrar la imagen correspondiente a cada acto y sus dos estados: pending
(sin completar) y done
(completado). Si queremos mostrar el acto dos completado lo que tenemos que hacer es tan sencillo como darle las clases "act act2 done"
.
Esto nos mostraría la porción de imagen correspondiente al acto 2 completado, que lo estamos controlando con la propiedad CSS de background-positon.
Ahora tenemos que crear los archivos y directorios que vamos a utilizar con este componente de progreso de la historia del juego. Cuando juegas a Diablo III puedes subir niveles y hacer misiones especiales sin necesidad de completar la historia del juego.
> 📗 Diablo 3 - Modos de juego: https://eu.diablo3.com/es/game/guide/gameplay/game-modes
Es por eso que implementamos este componente. Para ello, al mismo nivel del directorio /TopHeroes
, creamos un directorio llamado /ProgressList
. Dentro de este, creamos 2 ficheros: Index.vue
y ProgressItem.vue
.
📂 /MainBlock └──📂 /ProgressList ├── Index.vue └── ProgressItem.vue
El objeto que vamos a usar para pintar nuestro componente tiene este formato:
{ "progression": { "act1":true, "act3":true, "act2":true, "act5":true, "act4":true } }
Fíjate que no viene ordenado por actos, por lo que vamos a tener que realizar esta tarea de ordenamiento antes de poder iterar sobre el objeto de progression.
Lo primero que vamos a hacer es usar el componente (aunque esté vacío) en el componente padre, es decir, en /MainBlock/Index.vue
. Los 3 pasos de siempre. Importar, habilitar, utilizar.
// MainBlock/Index.vue // ... import ProgressList from './ProgressList/Index' export default { name: 'MainBlock', components: { ProgressList, HeroesList, TopHeroes } // ... }
Para usarlo, debajo del componente HeroesList
, agregamos lo siguiente:
<div class="grid-item item-left"> <TopHeroes v-if="hasHeroes" :heroes="topHeroes"/> <HeroesList v-if="hasHeroesList" :heroes="heroesList"/> <ProgressList :acts="profileData.progression"/> </div>
Al abrir la consola del navegador, es normal que veas errores o que la app no te funcione. Lo iremos corrigiendo a medida que vayamos avanzando.
Empezamos con el componente principal, es decir, con /ProgressList/Index.vue
:
<template> <div class="progression-bosses pt-4 mt-5 border-top"> <h2 class="font-diablo mb-4">Progression</h2> <b-row> <b-col v-for="(val, key) in sortedActs" :key="key" class="col-12 col-md-2"> <div class="bg-dark rounded mb-2"> <ProgressItem :act="{actNum: key, value: val}"/> </div> </b-col> </b-row> </div> </template> <script> import ProgressItem from './ProgressItem' export default { name: 'ProgressBosses', components: { ProgressItem }, props: { acts: { required: true, type: Object } }, computed: { /** * Order acts from 1 to 5 * @returns {Object} Ordered acts */ sortedActs () { return Object.keys(this.acts) .sort() .reduce((a, c) => { a[c] = this.acts[c] return a }, {}) } } } </script> <style lang="stylus"> .progression-bosses .boss-img display block .act margin 0 auto width 50px height 55px </style>
Todo lo que tiene este componente no requiere explicación, puesto que es un componente muy sencillo.
A modo de resumen este componente tiene definidas una prop, que son los actos, que los ordenamos a través de la función sortedActs (aunque en realidad es una computed property).
En el HTML iteramos sobre este objeto que contiene los actos ordenados del uno al cinco y cada elemento corresponde a otro componente (ProgressItem
, que vamos a ver ahora) que recibe como parámetro un objeto con el acto (ej. act2
) y el valor (ej. true
).
El componente ProgressItem.vue
tiene el siguiente contenido:
<template> <div class="d-flex flex-column" :title="actTitle"> <div class="boss-img pt-2"> <div class="act" :class="actClass"></div> </div> <p class="d-block text-center m-0 lead font-weight-bold">{{ fullActName }}</p> </div> </template> <script> const acts = { act1: 'I', act2: 'II', act3: 'III', act4: 'IV', act5: 'V' } export default { name: 'ProgressItem', props: { act: { required: true, type: Object, validator: (obj) => { return Object.keys(obj).length === 2 } } }, computed: { fullActName () { return `Act ${acts[this.act.actNum]}` }, actClass () { const status = this.act.value ? 'done' : 'pending' return `${this.act.actNum} ${status}` }, actTitle () { return this.act.value ? 'Act completed! 💃' : 'Act uncompleted 🙈' } } } </script>
Aunque este componente es bastante sencillo, vamos a comentar un par de cosas:
-
La prop act tiene una función de validación distinta a las que hemos visto anteriormente. No es gran cosa, pero está comprobando que el numero de claves del objeto que recibe sea igual a 2. Muy simple.
-
La computed property
actTitle
nos devuelve unString
dependiendo del valor del acto, es decir, si está completado o no. Para poder ver este texto, deberías dejar el ratón encima de dicho elemento unos segundos.
Lo diferencial de esto es que estamos incluyendo emojis (íconos) en el texto. ¡Sí! Los emojis los podemos usar como texto normal, usando las comillas, como si de un texto se tratara. 🤘😏🤘
> 📗 No soy muy fan de W3Schools, pero en este caso podemos hacer una excepción. Explicación de los emojis en la web: https://www.w3schools.com/html/html_emojis.asp
Exceptuando un par de cosas, estos componentes que acabamos de crear no tenían complejidad alguna. Si has seguido todos los pasos correctamente, la app debería verse así:
Y esto sería todo. Ahora vamos a empezar con el bloque de la derecha de nuestro grid, donde trabajaremos con los stats del usuario y el tiempo jugado por héroe.
Quédate con lo de los emojis en mente, pues los volveremos a utilizar más adelante.