> Manuales > Taller de HTML5

Rotación de múltiples iconos superpuestos para notificaciones de servicio.

En mi último artículo explicaba cómo se puede utilizar el API de Anclado de Sitios Web de IE9 para mostrar en la barra de tareas iconos superpuestos que sirven para avisar al usuario. La demo se centraba en cómo podemos mostrar un icono con un número, indicando que ha tenido lugar cierto evento (p.ej. la llegada de un mensaje al buzón de correo).


Sitio anclado a la barra de tareas con icono superpuesto

Se trata de una manera bastante interesante de conseguir que los usuarios sepan que en ese sitio web tienen información relevante para ellos y que deben visitarlo. Pero ¿qué pasa si un sitio web ofrece distintos tipos de notificaciones? A día de hoy los sitios web, en su mayoría, emplean estos mecanismos y para ellos es bastante normal que suministren distintos tipos de notificaciones, desde peticiones de amigos y recordatorios de eventos a la llegada de nuevos mensajes o invitaciones para jugar.

Rotación de múltiples iconos superpuestos

Lo bueno del API de anclado Site Pinning es que es muy flexible y con un poco de JavaScript podemos, con poco esfuerzo, mostrar varios iconos superpuestos para distintos servicios que podamos tener en nuestras páginas. En esta demo voy a ir rotando entre tres iconos superpuestos diferentes que avisan al usuario de que tiene pendientes mensajes, peticiones y acciones.

Como antes, tendré que echar mano de mi talento artístico para crear los iconos superpuestos, utilizando el editor x-icon. He diseñado cinco de cada y aquí se ve el aspecto que tienen los primeros de cada serie:

El código es algo distinto del que os mostraba en la última demo, para poder encajar diferentes tipos de datos en cada captura. Si bien en aquel ejemplo solo obtenía un bloque de datos, en esta demo voy a tener tres, uno por cada tipo de notificación:

myPin.init([{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 2 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 3 }] },
{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 1 }, { "label" : "Requests", "ntype" : "R", "num": 5 }, { "label" : "Actions", "ntype" : "A", "num": 2 }] },
{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 5 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 4 }] }
]);

Como recordatorio, el método getData() simula la captura de datos remotos. Así, si echas un vistazo al código anterior, verás que podemos simular que estamos obteniendo tres clases diferentes de datos. Por esa razón llamamos al método cada 10 segundos utilizando setInterval. Esto nos va a permitir ver el aspecto que tienen las notificaciones al cabo de un periodo de tiempo.

setInterval(function () { myPin.getData() }, 10000);

Lo siguiente que cambia es el uso de un timer para mantener un ligero retardo al mostrar en pantalla los iconos superpuestos. Con setTimeout() le añadimos un retardo suficiente, de modo que cualquier icono concreto sea visible al usuario antes de que pase en rotación al siguiente. Si no pusiéramos este retardo, la rotación sería tan rápida que el usuario no se enteraría del aviso. Si ves la siguiente imagen, este es el aspecto que van a tener nuestros avisos:


Icono superpuesto mostrando un aviso en forma de número

Esto lo conseguimos con el código siguiente:

// Grab the current set of data...
currData = this.dataBin[this.currIndex++].data;
/* We're going to display a new overlay every x number of seconds to display a new overlay icon so
let's loop through the data elements for the current set of data... */
for (var i=0; i < currData.length; i++ ){
(function(idx) { setTimeout( function(){ myPin.dispOverlay( currData[idx] ); }, 1000 * idx); }( i ));
}

Y esto es lo que sucede. En la primera línea me hago con el bloque de datos actual que lleva toda la información de notificaciones (mensajes, peticiones y acciones). Los datos serían estos:

[{ "label" : "Messages", "ntype" : "M", "num": 2 },
{ "label" : "Requests", "ntype" : "R", "num": 1 },
{ "label" : "Actions", "ntype" : "A", "num": 3 }]

Avanzamos en el bucle por cada grupo de datos y asigno un timer con setTimeout() que se encarga de hacer llamadas a dispOverlay() a intervalos aproximados de 1 segundo. Este es el código mágico que nos permite ir mostrando los iconos con un cierto retardo, como contaba antes. El efecto previsto es que el icono de ?mensajes? se muestra primero, seguido después, al cabo de 1 segundo, por el de ¿peticiones? y finalmente, por el de ¿acciones?.

Ahora, seguramente te estarás preguntando por qué tengo una función anónima que encapsula al setTimeout(). Esto es porque tengo un cierre dentro de setTimeout que podría generar una incidencia habitual de scoping en la que la variable ¿i?, que es la que utilizo para mantener el índice actual de los datos, solo se modifica con el último valor de índice. James Padolsey desarrolla una excelente explicación de esto y también debo agradecerle a John David Dalton su ayuda para resolverlo.

El cambio final se hace en dispOverlay() en donde tenemos que declarar el icono de superposición que se va a mostrar. Puesto que sabemos que tenemos tres tipos de avisos distintos, necesitamos una sentencia condicional que determine el tipo y genere el nombre de icono correspondiente:

if (theData.ntype == "M") {
oImg = "images/messages-" + theData.num + ".ico";
} else if (theData.ntype == "R") {
oImg = "images/requests-" + theData.num + ".ico";
} else if (theData.ntype == "A") {
oImg = "images/actions-" + theData.num + ".ico";
}

Este código comprueba el tipo y nos devuelve el icono adecuado y el número de notificaciones pendientes para ese tipo de aviso.

La demo y el código definitivo

La demo se puede probar con IE9 en este sitio web:
http://reybango.com/demos/sprotate/index.html

Cuando aparezca la página, arrastra la pestaña y suéltala en la barra de tareas para anclarla. Empezarás a ver que aparece una ventana nueva con el sitio anclado. Después empezarán a aparecer los iconos superpuestos en la barra de tareas e irán cambiando cada 10 segundos.

Aquí está el código fuente completo.

<!DOCTYPE html>
<html>
<head>
<title>Pinned Site - Rotating Overlay Icons</title>
<link rel="shortcut icon" type="image/ico" href="favicon.ico" />
<meta name="application-name" content="Pinned Site Test" />
<meta name="msapplication-starturl" content="http://reybango.com/demos/sprotate/index.html" />
<meta name="msapplication-navbutton-color" content="#3480C0" />
<meta name="msapplication-window" content="width=1024;height=768" />
<meta name="msapplication-tooltip" content="Testing the Pinned Site API" />
<style>
body {
background: none repeat scroll 0 0 #4492CE;
font: 440%/1.4em 'Segoe Light',Segoe,'Segoe UI','Meiryo Regular','Meiryo',sans-serif;
color: #EDEFF4;
}
</style>
</head>
<body>
<div>
<h1>Pinned Sites</h1>
<p>Rotating Overlay Icons</p>
</div>
<script>
var myData = [];
var myPin = {
currIndex: 0,
dataBin: [],
getData: function () {
var idx = 0, currData = [], cntr = 0, theData;
// Determines whether the current page was launched as a pinned site...
if (window.external.msIsSiteMode()) {
// Grab the current set of data...
currData = this.dataBin[this.currIndex++].data;
/* We're going to display a new overlay every x number of seconds to display a new overlay icon so
let's loop through the data elements for the current set of data... */
for (var i=0; i < currData.length; i++ ){
(function(idx) { setTimeout( function(){ myPin.dispOverlay( currData[idx] ); }, 1e3 * idx); }( i ));
}
if (this.currIndex > 2) { this.currIndex = 0 }
}
},
dispOverlay: function (theData) {
var oImg = "";
// Is there any data?
if (theData) {
// Clear any preexisting overlay icon
window.external.msSiteModeClearIconOverlay();
// Render the overlay icon based on the data returned...
if (theData.ntype == "M") {
oImg = "images/messages-" + theData.num + ".ico";
} else if (theData.ntype == "R") {
oImg = "images/requests-" + theData.num + ".ico";
} else if (theData.ntype == "A") {
oImg = "images/actions-" + theData.num + ".ico";
}
// Go ahead and create the overlay image and it's label...
this.setOverlay(oImg, theData.label);
}
},
setOverlay: function (icon, desc) {
// Sets the overlay icons...
window.external.msSiteModeSetIconOverlay(icon, desc);
window.external.msSiteModeActivate();
},
init: function (myData) {
this.dataBin = myData;
this.getData();
}
};
// This clears out any previously set overlay icons...
window.external.msSiteModeClearIconOverlay();
// Run it once to kick everything off...
myPin.init([{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 2 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 3 }] },
{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 1 }, { "label" : "Requests", "ntype" : "R", "num": 5 }, { "label" : "Actions", "ntype" : "A", "num": 2 }] },
{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 5 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 4 }] }
]);
// This is only here because I want to simulate pulling data on a regular interval...
setInterval(function () { myPin.getData() }, 10000);
</script>
</body>
</html>

Rey Bango

Technical Evangelist en Microsoft

Manual