Cómo Crear un Prototipo de Aplicación Usando CSS y JavaScript
() translation by (you can also view the original English article)
La animación es realmente una de las mejores características en llegar a CSS en mucho tiempo. Después de todo, como nos hemos dado cuenta, el movimiento puede mejorar la experiencia de usuario y motivar acciones en donde los contextos estáticos se quedan cortos. En este tutorial vamos a construir un prototipo para una aplicación usando animaciones CSS y un toque de JavaScript.
Lo Que Estamos Creando
Para este ejercicio desarrollaremos un prototipo para una aplicación que permite a los suscriptores seguirse unos a otros. Inicialmente, cargaremos una lista de usuarios con sus nombres debajo de cada avatar correspondiente. Esos avatares, cuando se les da clic, dispararán un modal conteniendo un botón "seguir" e información adicional perteneciente a cada usuario individual.
Para propósitos de demostración cargaremos 30 usuarios usando un servicio llamado Generador Aleatorio de Usuarios. Su API no solo proporciona imágenes aleatorias para usuarios, sino que también proporciona datos aleatorios como nombres, direcciones, correos electrónicos y mucho más.
1. Establece el Escenario
He discutido opciones anteriormente para construir conceptos de prototipo, pero para este artículo construiremos un prototipo con código real. Para esta tarea estaremos usando Marvel Devices; una librería de código abierto que contiene dispositivos móviles de CSS puro para mostrar prototipos típicamente hechos con Marvel App.



Para comenzar incluye devices.min.css en la parte superior de tu documento, regresa a la demostración y selecciona la combinación que quieres, después, copia y pega el HTML generado en tu proyecto.
2. Carga los Usuarios
Con el CSS obtenido para nuestro dispositivo de demostración (Nexus 5) es tiempo de trabajar en cargar los usuarios de nuestra aplicación.
1 |
<div class="marvel-device nexus5"> |
2 |
<div class="top-bar"></div> |
3 |
<div class="sleep"></div> |
4 |
<div class="volume"></div> |
5 |
<div class="camera"></div> |
6 |
<div class="screen"> |
7 |
<h3 class="title">Results</h3> |
8 |
<ul class="users"></ul> |
9 |
</div>
|
10 |
</div>
|
Los usuarios serán cargados como elementos de lista e inyectados en la lista sin orden .users
. Esta es la base inicial, pero necesitamos usar JavaScript para comunicar con la API de usuario aleatorio para poder insertar estos elementos de lista. Para hacer esta petición usaremos JavaScript plano estilo ES6.
1 |
let request = new XMLHttpRequest(); |
El request
es la variable que contendrá nuestra petición AJAX, pero requiere una URL con la cuál conectar.
1 |
request.open('GET', 'https://randomuser.me/api/?results=30', true); |
El método open
regresará los datos de la API desde la URL y los parámetros que establecimos. Hay muchos otros parámetros para usar, pero para esta demostración solo regresaremos 30 resultados. Ahora que tenemos nuestra URL en su lugar y la petición enviada, regresemos esa información.
1 |
// Utility for DOM selection
|
2 |
function select(s) { |
3 |
return document.querySelector(s); |
4 |
}
|
5 |
|
6 |
// Store for referencing
|
7 |
const users = select('.users'), |
8 |
|
9 |
// AJAX Request
|
10 |
request.onload = function() { |
11 |
if (request.status >= 200 && request.status < 400) { |
12 |
|
13 |
let data = JSON.parse(request.responseText), |
14 |
l = data.results.length; |
15 |
|
16 |
for(var i = 0; i < l; i++) { |
17 |
// data results
|
18 |
console.log(data.results[i]); |
19 |
}
|
20 |
|
21 |
} else { |
22 |
alert('We reached our target server, but there was an error'); |
23 |
}
|
24 |
};
|
25 |
|
26 |
request.onerror = function() { |
27 |
alert('There was a connection error of some sort') |
28 |
};
|
29 |
|
30 |
// Send Ajax call
|
31 |
request.send(); |
La petición entera está ahora en posición. Abriendo la consola verás todos los resultados de los datos proporcionados listados. Este es un buen comienzo, pero definitivamente necesitamos hacer más que solo registrar la salida a la consola.
3. Inyectar Datos de Usuario
Para esta siguiente parte, inyectaremos marcado usando los datos que tenemos disponibles de la API de usuarios aleatorios.
1 |
users.insertAdjacentHTML('beforeend', '<li><img src="'+ |
2 |
data.results[i].picture.medium +'" data-pic="'+ |
3 |
data.results[i].picture.large +'" data-name="'+ |
4 |
data.results[i].name.first + ' ' + |
5 |
data.results[i].name.last + '" data-email="'+ |
6 |
data.results[i].email +'"><span class="user-name">'+ |
7 |
data.results[i].name.first +'</span></li>'); |
Las líneas de código de arriba serán colocadas en el ciclo creado anteriormente que estaba registrando datos a la consola. Como se mencionó, tenemos unas cuantas opciones en relación a los datos adjuntos a los objetos de usuario, como un primer nombre, correo electrónico y tamaños de avatar. El tamaño mediano de imagen será usado para mostrar inicialmente, mientras que el tamaño grande será usado para nuestro modal que es disparado cuando el avatar miniatura es presionado. Todas estas piezas de información serán almacenadas dentro de atributos de datos para que podamos recogerlos según sea necesario.
4. Detecta la Posición de Avatar
Vamos a construir otro componente para esta demostración; un modal es disparado desde el punto de ejecución (ej. dando clic sobre un avatar). Necesitaremos a nuestro querido amigo matemáticas para realmente determinar de donde se expandirá el modal.
1 |
var transOriginNames = { |
2 |
webkitTransformOrigin : 'webkitTransformOrigin', |
3 |
MozTransformOrigin : 'MozTransformOrigin', |
4 |
msTransformOrigin : 'msTransformOrigin', |
5 |
transformOrigin : 'transformOrigin' |
6 |
};
|
7 |
|
8 |
users.addEventListener('click', function(e) { |
9 |
let target = e.target, |
10 |
target_coords = target.getBoundingClientRect(); |
11 |
|
12 |
if(target.nodeName === 'IMG') { |
13 |
for(var name in transOriginNames) { |
14 |
modal.style[name] = (target.offsetLeft + (target_coords.width/2)) +'px ' + ((target.offsetTop + (target_coords.height/2)) - screen_scroll.scrollTop) + 'px'; |
15 |
}
|
16 |
}
|
17 |
});
|
Para lograr esta expansión del modal desde el punto de ejecución necesitamos asegurar que nuestro transform-origin
es correcto para poder escalar apropiadamente el modal. Estoy usando un object
para contener todos nuestros prefijos transform-origin
ya que estamos usando JavaScript para establecerlos y también necesitamos asegurar que todas las bases están cubiertas para navegadores que no soportan el estándar.
Toma nota de las matemáticas requeridas para determinar los valores transform-origin
.
1 |
modal.style[name] = (target.offsetLeft + (target_coords.width/2)) +'px ' + ((target.offsetTop + (target_coords.height/2)) - screen_scroll.scrollTop) + 'px'; |



El diagrama de arriba explica visualmente cómo se están calculando las matemáticas. Para poder determinar la ubicación correcta usamos offsetLeft
y offsetTop
más la mitad de width
y height
respectivamente para encontrar el centro exacto del avatar. scrollTop
también es muy importante porque 30 usuarios desbordarán los límites de la pantalla del dispositivo y offsetTop
necesitará tener este valor restado para determinar la distancia apropiada de la parte superior de la pantalla. Todos los valores en el diagrama de arriba son proporcionados por nuestro amigo getBoundingClientRect
.



Registrando target_coords
a la consola puedes ver todas las dimensiones y valores que requerimos para hacer una suposición apropiada. Estos valores con relación a width
y height
siempre cambiarán dependiendo de la posición del avatar dentro de la pantalla del dispositivo.
5. Anima el Modal
Con las coordenadas del avatar prepuestas y listas para recibir nuestro evento clic, es tiempo de agregar el movimiento modal que mostrará el perfil de usuario.
1 |
.modal { |
2 |
transform: scale(0) translateZ(0); |
3 |
transition-duration: 320ms; |
4 |
transition-timing-function: cubic-bezier(.4, 0, .2, 1); |
5 |
transition-property: transform, opacity; |
6 |
opacity: 0; |
7 |
}
|
El código de arriba está reducido de la demostración para mostrar las propiedades exactas de movimiento que vamos a estar usando. Estamos escondiendo inicialmente usando opacity
y scale
.
Ahora es tiempo de definir las propiedades CSS que manejarán el estado activo para el movimiento del modal.
1 |
.screen.active .modal { |
2 |
transform: scale(1) translateZ(0); |
3 |
opacity: 1; |
4 |
}
|
Usando JavaScript vamos a intercalar una clase active
en screen
. En las líneas de arriba estamos revisando lo que establecimos anteriormente. Así es como creamos el efecto de expansión del modal. Es un estilo muy de Google Material Design que ayuda desde interrumpir el comportamiento cognitivo y rápidamente responde a la entrada del usuario de manera precisa haciendo responsivo al movimiento. Este estilo también confirma la entrada de usuario expandiendo inmediatamente hacia afuera del punto de contacto.
6. Dale Movimiento a los Avatares
Durante la carga crearemos un efecto de escalamiento haciendo la secuencia menos estática y más responsiva. Cada avatar escalará en un intervalo diferente que el siguiente y para eso usaremos CSS.
1 |
@keyframes fade-in { |
2 |
from { opacity: 0; } |
3 |
to { opacity: 1; } |
4 |
}
|
5 |
|
6 |
@keyframes expand-out { |
7 |
from { transform: scale(0); } |
8 |
to { transform: scale(1); } |
9 |
}
|
10 |
|
11 |
.users li { |
12 |
opacity: 0; |
13 |
}
|
Inicialmente ocultaremos los usuarios y después intercalaremos una clase show
una vez que la petición AJAX esté lista. También he incluido nuestros fotogramas que transformarán nuestras propiedades a los valores correctos. Usaremos estos fotogramas en el siguiente pedazo de código que establece el movimiento en reproducción.
1 |
$user-count: 30; |
2 |
$duration: 200ms; |
3 |
$stagger_delay: 0.0125s; |
4 |
$easing: cubic-bezier(.66, .14, .83, .67); |
5 |
|
6 |
.users.show { |
7 |
li { |
8 |
animation-duration: $duration; |
9 |
animation-name: fade-in; |
10 |
animation-fill-mode: both; |
11 |
animation-timing-function: $easing; |
12 |
opacity: 1; |
13 |
|
14 |
img { |
15 |
animation-duration: $duration; |
16 |
animation-name: expand-out; |
17 |
animation-fill-mode: both; |
18 |
animation-timing-function: $easing; |
19 |
}
|
20 |
|
21 |
@for $i from 1 through $user-count { |
22 |
&:nth-of-type(#{$i}) { |
23 |
animation-delay: ($stagger_delay * $i); |
24 |
img { |
25 |
animation-delay: ($stagger_delay * $i); |
26 |
}
|
27 |
}
|
28 |
}
|
29 |
}
|
30 |
}
|
Para ayudar con las matemáticas estoy usando ciclos Sass y variables para contener la cuenta de nuestros usuarios que también está atada con nuestros resultados JS desde la llamada de API de usuario aleatorio. El ciclo es la clave de todo el rompecabezas ya que ciclará sobre nuestra cuenta de usuarios y agregará el valor que definimos en una variable.



Arriba está nuestro resultado con la secuencia final de animación de avatar. Recuerda que solo estamos usando animaciones de fotograma en CSS y usando JavaScript para intercalar la clase show
una vez que los avatares son recuperados desde la API de usuario aleatorio.
Ideas de Cierre
Asegúrate de ver la demostración ya que ahí también hay elementos adicionales que agregan beneficios, tales como un cargador animado que mostrará mientras los usuarios está cargando y se remueve a el mismo una vez que los avatares están cargados.
Gracias a Shaw por su visión y esta toma Dribble usada para inspiración. Como siempre deja cualquier comentario abajo para discusión futura, ¡y dale a esta demostración algunos corazones en CodePen si quieres ver más como este!