Listas con imágenes en jQueryMobile

Vamos con una entrada sobre jQueryMobile, que hace que no le damos a la Mobile Web (sigo a la espera de que empiece el curso de Audacity, al que me apunté ya en agosto…). Ya hablamos de cómo hacer listas, desde luego, pero ¿cómo hacer listas con un pequeño thumbnail a la izquierda? Es muy simple, de hecho.

Como siempre empiezas descargando jQuery y jQueryMobile y añadiéndolos a tu código. Luego en el código metes una lista aplicándole un par de clases… pero eso mejor lo vemos con ejemplos de código. En este primer ejemplo veremos una lista donde cada elemento es un enlace, con una foto en la izquierda y un texto grande:

<ul data-role="listview">/*A la lista le aplicamos el data-role listview*/
  <li><a href="#1"><img src="img/foto1.png" width="100" height="100"/><h3>Elemento1</h3></a></li> /*En cada elemento metemos un enlace, una imagen (tamaño 100+100) y un título h3*/
  <li><a href="#2"><img src="img/foto2.png" width="100" height="100"/><h3>Elemento2</h3></a></li>
  <li><a href="#3"><img src="img/foto3.png" width="100" height="100"/><h3>Elemento3</h3></a></li>
</ul>

Esto es muy básico, vamos con algo más chulo: el mismo ejemplo, pero con un texto más pequeño debajo del título y con divisores:

<ul data-role="listview" data-inset="true" data-autodividers="true" data-filter="true">/*A la lista le aplicamos el data-role listview y el resto de configuración*/
  <li><a href="#1"><img src="img/foto1.png" width="100" height="100"/><h3>Elemento1</h3>
<p>Este es el primer elemento</p></a></li> /*En cada elemento metemos un enlace, una imagen (tamaño 100+100) y un título h3, y además una pequeña descripción*/
  <li><a href="#2"><img src="img/foto2.png" width="100" height="100"/><h3>Elemento2</h3>
<p>Este es el segundo elemento</p></a></li>
  <li><a href="#3"><img src="img/foto3.png" width="100" height="100"/><h3>Elemento3</h3>
<p>Este es el tercer elemento</p></a></li>
  <li><a href="#3"><img src="img/foto3.png" width="100" height="100"/><h3>Forma1</h3>
<p>Este es un elemento con otra letra para que se vea el divider</p></a></li> /**Y este último... para que se vea el divisor*/
</ul>

Y con este simple ejemplo puedes darle vida a tus listas.

Tutorial de IdexedDB

El almacenamiento en el navegador se está convirtiendo en algo fundamental para el desarrollo de aplicaciones web con HTML5 y Javascript. En un primer momento parecía que WebSQL se iba a convertir en el standar, apoyado por Google y Apple en sus navegadores basados en Webkit. Pero la oposición de Microsoft y, sobre todo, de Mozilla sumada al auge de las bases de datos no relacionales en el ámbito del desarrollo móvil, ha hecho que IndexedDB se postule como el nuevo standar para almacenamiento en estos desarrollos. Desde 2010 la W3C ha dado por obsoleto WebSQL.

Todavía no me he puesto a trabajar con él en profundidad, pero os traigo aquí un tutorial basado en este de HTML5Rocks donde se nos explica cómo migrar de WebSQL a IndexedDB.

El ejemplo es una base de datos para un TODO list. Con WebSQL simplemente operábamos como si de una base de datos SQL se tratara, pero el concepto de IdexedDB es distinto. Se trata de un almacén de objetos Javascript donde guardamos parejas de valores: una clave y el objeto. Las búsquedas no se hacen con una query sino que se realiza una consulta sobre un índice que luego es iterado.

El primer paso con IdexedDB es abrir una base de datos:

window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;

// Manejando el prefijo de Chrome para IDBTransaction/IDBKeyRange.
if ('webkitIndexedDB' in window) {
  window.IDBTransaction = window.webkitIDBTransaction;
  window.IDBKeyRange = window.webkitIDBKeyRange;
}

indexedDB.db = null;

// Para depurar enviamos los errores a la consola
indexedDB.onerror = function(e) {
  console.log(e);
};

Arriba ya tenemos abierta una base de datos. Lo segundo, y también fundamental, es crear un almacén de objetos, igual que creamos tablas cuando trabajamos con una base de datos SQL.

indexedDB.open = function() {
  var request = indexedDB.open("todos");

  request.onsuccess = function(e) {
    var v = "2.0 beta"; // puedes poner cadenas en el nombre de la versión
    todoDB.indexedDB.db = e.target.result;
    var db = todoDB.indexedDB.db;
    // En la transacción setVersión es donde podemos crear los almacenes
    if (v!= db.version) {
      var setVrequest = db.setVersion(v);

      // para poder crear almacenes tenemos que hacerlo en onsuccess
      setVrequest.onfailure = todoDB.indexedDB.onerror;
      setVrequest.onsuccess = function(e) {
        if (db.objectStoreNames.contains("todo")) {
          db.deleteObjectStore("todo");
        }

        var store = db.createObjectStore("todo", {keyPath: "timeStamp"});

        var transaction = e.target.result;
        transaction.oncomplete = function() {
          todoDB.indexedDB.getAllTodoItems();
        }
      };
    } else {
      todoDB.indexedDB.getAllTodoItems();
    }
  };

  request.onfailure = todoDB.indexedDB.onerror;
};

Aquí hemos abierto el almacén todos, tras ello comprobamos que la versión de la base de datos sea diferente a la que estamos definiendo. En caso de que no lo sea, con setVersion creamos el nuevo almacén. El único sitio donde podemos modificar la estructura de la base de datos es en setVersion, donde se nos permite crear y eliminar almacenes de objetos.

El siguiente paso es ver cómo podemos añadir objetos a la colección. Para ello creamos la función addTodo.

indexedDB.addTodo = function() {
  var db = todoDB.indexedDB.db;
  var trans = db.transaction(['todo'], 'readwrite');
  var store = trans.objectStore('todo');

  var data = {
    "text": todoText, // el texto debe ser visible aquí
    "timeStamp": new Date().getTime()
  };

  var request = store.put(data);

  request.onsuccess = function(e) {
    todoDB.indexedDB.getAllTodoItems();
  };

  request.onerror = function(e) {
    console.log("Error Adding: ", e);
  };
};

Este método addTodo comienza cogiendo una referencia a la base de datos, seguimos definiendo una transacción readwrite y obtenemos una referencia a la coleccion de objetos. Luego creamos los datos como un objeto JSON y lo metemos en la base con el método put. Guardaremos el texto y un timestamp que servirá como clave única. Definimos que se re-renderizará en caso de éxito y que en caso de error se enviará a la consola.

Y si podemos insertar datos, desde luego podemos también borrarlos. Para ello crearemos el método deleteTodos:

indexedDB.deleteTodo = function(id, text) {
  if (confirm("Are you sure you want to Delete " + text + "?")) {
    var db = todoDB.indexedDB.db;
    var trans = db.transaction(["todo"], 'readwrite');
    var store = trans.objectStore("todo");

    var request = store.delete(id);

    request.onsuccess = function(e) {
      todoDB.indexedDB.getAllTodoItems();
    };

    request.onerror = function(e) {
      console.log("Error Adding: ", e);
    };
  }
};

El funcionamiento es similar al anterior método, pero con un delete en lugar del put, que recibe la clave del objeto para ser borrado.

Finalmente vamos con la lectura e impresioń de datos en pantalla, que a fin de cuentas es lo que nos importa en estos casos. Si queremos almacenar algo es para poder recuperarlo posteriormente.

function showAll() {
  document.getElementById("ourList").innerHTML = "";

  var request = window.indexedDB.open("todos");
  request.onsuccess = function(event) {
    // Enumerar el almacén de objetos completo.
    var db = todoDB.indexedDB.db;
    var trans = db.transaction(["todo"], 'readonly');
    var request = trans.objectStore("todo").openCursor();
    var ul = document.createElement("ul");

    request.onsuccess = function(event) {
      // Esto es un hack para versiones antiguas de Firefox (older versions than 6)
      var cursor = request.result || event.result;

      // Cuando el cursor esté a nulo es que hemos terminado la enumeración, por lo que
      // actualizamos el DOM
      if (!cursor) {
        document.getElementById("ourList").appendChild(ul);
        return;
      }

      var li = document.createElement("div");
      li.textContent = "key: " + cursor.key + " => Todo text: " + cursor.value.text;
      ul.appendChild(li);
      cursor.continue();
    }
  }
}

La idea en el caso de mostrar los resultados es abrir la transacción como readonly, creamos una lista (para este caso) y en cada iteración del cursor creamos un elemento li con un div dentro con los datos que vamos agregando a la lista. Cuando se termine el cursor, en la última iteración, añadimos la lista al DOM.

Como apreciación he de decir que IndexedDB, aunque desde luego es muy útil, tiene cosas que me parecen innecesariamente complicadas. Tal vez sea porque vengo del mundo de las bases de datos SQL pero WebSQL me parecía más claro y usable. Y con todo yo estoy acostumbrado a trabajar con Javascript (reconozco que nunca cosas muy complejas), no quiero imaginarme a muchos programadores que no tocan jamás el lado del cliente.

DonnieTabs, plugin simple de pestañas para jQuery

El plugin DonnieTabs, a cargo del menda, está disponible con licencia MIT en GitHub para que lo utilicéis, modifiquéis y forkeéis a vuestro gusto.

Su uso es simple:

Añades en tu web la librería jQuery, el archivo .js de DonnieTabs y el archivo CSS (y si necesitas el CSS extra para IE también viene).

Luego para crear las pestañas y el contenido simplemente tenéis que usar unas clases y nomenclatura concretos en el marcado HTML y punto:

<!-- Para las pestañas una lista y dentro cada elemento así-->
<ul class="simpletabs">
    <li>
	<a href="#tab1">Tab1</a>
    </li>
    <li>
	<a href="#tab2">Tab2</a>
    </li>
    <li>
	<a href="#tab3">Third tab</a>
    </li>						
</ul>
<!-- La lista debe ser la clase SimpleTabs, y la referencia del enlace debe ser al Id de la pestaña de contenido-->

Con eso tenéis las pestañas de navegación, la parte del contenido irá así:

<!-- Todo el contenido irá dentro un div de la clase tab_container -->
<div class="tab_container">
<!-- Y dentro, el contenido va en divs de la clase tab_content-->
    <div id="tab1" class="tab_content">
	<h3>titulo</h3>
	<p>Contenido</p>	
    </div>
    <div id="tab2" class="tab_content">
	<h3>titulo</h3>
	<p>Contenido</p>
    </div>
    <div id="tab3" class="tab_content">
	<h3>titulo</h3>
	<p>Contenido</p>
    </div>			
</div>	

Con esto ya tenéis vuestra página con navegación por pestañas. Podéis aportar todo lo que queráis a este proyecto. Yo intentaré meterle más cosas, por ahora es algo muy básico y ya tengo alguna idea.

Colorbox: Plugin de jQuery para ventanas modales y lightbox, ahora en gallego

Español:

Soy habitual usuario de Colorbox, plugin que he utilizado en varios desarrollos (Loterías Cedeira, Lana y Punto) para la creación de ventanas modales con contenido externo o con galerías de fotos, una alternativa a las ventanas modales de jQueryUi o a Lightbox. Desde hoy además incluye traducción al gallego dentro de los complementos de internalización (gracias al menda, que vale que no era mucho pero nadie lo había hecho). Podéis descargarlo desde este enlace.

Galego:

Son un devoto usuario de Colorbox , plugin que xa utilicéi en varios desenvolvementos (Loterías Cedeira, Lana Y Punto) para xerar fiestras modáis con contido externo ou con galerías de imaxes, unha alternativa ás fiestras modáis de jQueryUi ou ó Lightbox. Dende hoxe además inclúe a traducción ó galego dentro dos seus complementos de internacionalización (grazas a min, que vale que non era moito choio pero ninguén o fixera antes). Podedes descargalo dende esta ligazón.

Plugins de subida múltiple de archivos con jQuery

Si bien HTML5 ya incluye un campo «multi» para subir múltiples archivos de una sola vez los navegadores tienden a ponerse puñeteros con el tema, así que de momento Javascript es la mejor solución. Estos días me he visto en la necesidad de recurrir a uno de estos plugins, y he encontrado cosas interesantes:

Como estética ninguno gana a jQuery File Upload, integadro tanto con Bootstrap como con jQueryUi, con una presentación sumamente atractiva. Eso sí, tendrás que pasar por caja porque no se trata de un plugin libre (lo que en mi caso me llevó a descartarlo). Tres cuartos de lo mismo con FineUploader, muy chulo pero tendrás que pagar.

Si nos vamos al campo de lo libre, tenemos Plupload bajo licencia GPLv2, un plugin que permite incluso meter elementos Flash, Silverlight y HTML5 juntos para que funcionen como «fallback» en caso de que el navegador no soporte alguno.

Y finalmente, por cuestiones de flexibilidad, acabé por decantarme por Multiple File Upload Plugin, que está claro que no es el más bonito, pero sí ligero, xbrowser y flexible. Fácilmente personalizable tanto en temas de traducciones como a la hora de trabajar con la subida o de modificar su apariencia.

Estas son las que me han parecido las mejores opciones a la hora de trabajar con subida múltiple, entre lo que he podido ver. Ahora cada cual que busque, pruebe y elija (Wood).

Poblar un combo dinámicamente con jQuery y JSON

Es habitual que, creando formularios, nos encontremos con la situación de tener dos combos (o campo select si lo preferís) y que uno tenga que cargar/modificar sus datos según el resultado seleccionado en el otro, de forma dinámica. Esto es posible con Javascript, y muy cómodo si utilizamos JSON y jQuery.

Os planteo un ejemplo simple: tenemos dos combos, uno con provincias y otro con ayuntamientos. El marcado HTML va a ser más o menos tal que así:

<select id="provincias" name="provincias">
  <option value=""></option>
  <option value="1">A Coruña</option>
  <option value="2">Lugo</option>
  <option value="3">Ourense</option>
  <option value="4">Pontevedra</option>
</select>
<select id="poblaciones" name="poblaciones" disabled="disabled">
</select>

Como véis, al momento de cargar la página el campo provincias tendrá las cuatro provincias gallegas, y el campo poblaciones estará desactivado. ¿Ahora qué necesitamos? Pues primero necesitamos un script que nos saque las poblaciones de la base de datos y nos las envíe como un JSON. Cualquier lenguaje de lado del servidor nos vale, para el ejemplo va a ser PHP (pero vamos, que podría aplicarse con Java, Ruby, node.js, Python…). Crearemos un script llamado getPoblacionesJson.php

<?php
include 'conexionbd.php';
if ($mysqli -> multi_query("CALL sp_GetPoblaciones(" . $_GET['pr'] . ")")) {
	$poblaciones = array();
	do {
		if ($result = $mysqli -> store_result()) {
			while ($fila = $result -> fetch_assoc()) {				
				$poblaciones[$fila['Id']] = $fila['Nombre'];
			}
		}
	} while($mysqli->next_result());
	print_r(json_encode($poblaciones));
}
?>

En este caso veis que lo que hacemos es llamar a un procedimiento almacenado que nos devuelve las provincias, recorremos el resultado y vamos guardándolo en un array. Finalmente lo convertimos a JSON y lo imprimios para que lo recoja la función de Javascript. Si os estáis preguntando cómo va el procedimiento almacenado, es una simple select en MySQL, tal que así:

DELIMITER $$
CREATE PROCEDURE sp_GetPoblaciones(IN provincia int)
begin
	SELECT Id, Nombre FROM poblaciones WHERE (provincia is null or IdProvincia = provincia) ORDER BY Nombre;
end $$
DELIMITER ;

Ok, tenemos entonces el script del servidor, el marcado y el procedimiento en la base de datos. ¿Qué nos queda? Pues el Javascript, vitaminado con jQuery para ganar productividad:

$("#provincias").change(function() {
	$("#poblaciones").empty();
	$.getJSON('http://localhost/getPoblacionesJson.php?pr='+$("#provincias").val(),function(data){
		console.log(JSON.stringify(data));
		$.each(data, function(k,v){
			$("#poblaciones").append("<option value=\""+k+"\">"+v+"</option>");
		}).removeAttr("disabled");
	});
});

La idea es simple: Si se registra algún cambio en el combo de provincias vaciamos el combo de poblaciones y lo rellenamos con los nuevos datos, obtenidos mediante la función getJSON() y que recorreremos con each() como un conjunto de claves/valores. Finalmente, por si está desactivado, lo reactivamos. He hecho un console.log por si queréis ver cómo funciona la cosa en la consola de javascript de Chrome o del Firebug.

Espero que os sirva de ayuda esta entrada para trabajar con combos dinámicos.

AngularJS: animaciones con scroll

No tenía muy claro cómo titular esta entrada, dado que su título original es AngularJS: Scroll Animations. Como en ocasiones anteriores se trata de una «traducción-no-literal»/explicación de un artículo tutorial en inglés.

En fin, ¿a qué nos referimos con esto de animaciones con scroll? Seguro que habéis visto más de una página que las tiene. Se trata de animaciones que se activan conforme vas haciendo scroll en la página, al alcanzar una determinada posición. La web Let’s Free Congress es presentada con ejemplo de esto en el texto original.

AngularJS logo

La idea es que la animación comience cuando el usuario llegue a la parte de la pantalla, para lo cual nos serviremos de la directiva scrollPosition, que se nos presenta así en el ejemplo original

.directive('scrollPosition', ['$window', '$timeout', '$parse', function($window, $timeout, $parse) {
    return function(scope, element, attrs) {

        var windowEl = angular.element($window)[0];
        var directionMap = {
          "up": 1,
          "down": -1,
          "left": 1,
          "right": -1
        };

        // Recuperamos el elemento con el scroll
        scope.element = angular.element(element)[0];

        //Almacenamos los elementos que escuchan a este evento
        windowEl._elementsList = $window._elementsList || [];
        windowEl._elementsList.push({element: scope.element, scope: scope, attrs: attrs});

        var element, direction, index, model, scrollAnimationFunction, tmpYOffset = 0, tmpXOffset = 0;
        var userViewportOffset = 200;

        function triggerScrollFunctions() {

          for (var i = windowEl._elementsList.length - 1; i >= 0; i--) {
            element = windowEl._elementsList[i].element;
            if(!element.firedAnimation) {
              directionY = tmpYOffset - windowEl.pageYOffset > 0 ? "up" : "down";
              directionX = tmpXOffset - windowEl.pageXOffset > 0 ? "left" : "right";
              tmpXOffset = windowEl.pageXOffset;  
              tmpYOffset = windowEl.pageYOffset;  
              if(element.offsetTop - userViewportOffset < windowEl.pageYOffset && element.offsetHeight > (windowEl.pageYOffset - element.offsetTop)) {
                model = $parse(windowEl._elementsList[i].attrs.scrollAnimation)
                scrollAnimationFunction = model(windowEl._elementsList[i].scope)
                windowEl._elementsList[i].scope.$apply(function() {
                  element.firedAnimation = scrollAnimationFunction(directionMap[directionX]);  
                })
                if(element.firedAnimation) {
                  windowEl._elementsList.splice(i, 1);
                }
              }
            } else {
              index = windowEl._elementsList.indexOf(element); //TODO: Add indexOf polyfill for IE9 
              if(index > 0) windowEl._elementsList.splice(index, 1);
            }
          };
        };
        windowEl.onscroll = triggerScrollFunctions;
      };   
    }]);

Tal directiva se ha utilizado en este ejemplo. En ella podéis ver como la barra de la pantalla del móvil progresa según bajáis la barra.

En fin, la cosa es explicar ahora como usar esto. Como en el ejemplo original, iremos desgranando poco a poco el código, despedazándolo para ver cómo funciona.

Lo principal es añadir la directiva al elemento que queremos animar:

.row.show-for-large-up
  .teaser(scroll-position, scroll-animation='fireupApplicationDesignAnimation')
    .row
      .large-6.columns.left-align.margin-top
        h1(data-i18n="_CareerDesign_APPLICATIONDESIGNTITLE")

Esto le dice a AngularJS que cuando el usuario llegue al área determinada del DOM debe dispararse la animación. En vuestro controlador deberíais tener algo así

$scope.fireupApplicationDesignAnimation = function(scrollDirection) {
        scrollDirection > 0 ? reduceAmount() : aumentAmount(); // We want to increase on scrollDown
        setOffsetForImage();
    };

La directiva envía al controlador la dirección del scroll, lo que permite mostrar animaciones basadas en él. En ocasiones querrás ejecutar la animación sólo una vez en lugar de que ocurra cada vez que el scroll esté a su altura. Puedes lograrlo devolviendo un valor true y chequeándolo antes de lanzar la animación, como en este ejemplo:

$scope.fireupMarketingDesignAnimation = function() {
      if(!firedMarketingAnimation) {
        window.animations.marketingAnimation.init();
        firedMarketingAnimation = true;
        return firedMarketingAnimation;  
      }
    }

Como podéis ver en el ejemplo la función que lanza la animación está dentro de un if que hará que sólo se ejecute una vez.

En fin, resumiendo ¿Cómo funciona la directiva?. El proceso es el siguiente:

  • Crea un array de elementos que requieren un eventListener del tipo onScroll. Puedes tener varios almacenados, y si devuelves un true para una sola ejecución serán eliminados para reducir el uso de memoria.
  • Añade un eventListener onScroll que comprueba en qué parte de la página está el usuario.
  • Recorre tus elementos vinculados con la directiva comprobando si se han activado las animaciones.
  • Si no lo han hecho y el usuario se posiciona junto a ellas con el scroll entonces se activan. Esto obliga a hacer mucho uso de $parse, así que léete bien su documentación para entenderlo al 100%.
  • Si la animación llamada a través de scope devuelve true, la extrae del array, como dijimos en el primer punto.

La conclusión:

La directiva es útil cuando necesitas lanzar varias animaciones en un periodo de tiempo específico. También te permitirá definir un comportamiento específico según la posición del scroll o hasta para realizar cambios en propiedades CSS3 relacionándolas con la posición de la página.

En este ejemplo en Codepen, creado por el autor del artículo original, podéis ver cómo también funciona en horizontal. Si movéis el scroll lentamente veréis como el balón lo sigue. Eso sí, probad con Chrome porque a mi en Firefox en lugar de una pelota se me ve una especie de mojón raro.

En fin, finalmente recomendar el blog de jjperezaguinaga Surviving by Coding, donde podréis encontrar mucha info sobre AngularJS y otros temas de desarrollo web, en inglés.

AngularJS: Introducción y Hello World

Llevo unos días trasteando con el framework de Javascript AngularJS, libre y mantenido por Google. En su momento no lo incluí en mi pequeña selección de frameworks a ir mirando, pero al final he acabado por dedicarme a trastear con él. De momento tampoco he avanzado mucho, que este mes estoy liado, pero en el futuro os iré comentando.

Comencemos por la primera pregunta ¿para qué vale AngularJs? En fin, la idea de este framework es dinamizar las páginas HTML estáticas vinculando elementos de ese HTML con un modelo de datos que hemos definido en Javascript (a mano, o desde un JSON), permitiendo la interactividad con el usuario. En este caso, aunque se usa un patrón MVC la potencia es que nos permite, mediante bindings {{}} , acceder a la funcionalidad o a los datos. Es cierto que el resultado final lo podemos conseguir de otras muchas formas, pero AngularJS nos ahorrará muchas líneas de código. Algunos lo comparan ya con lo que significó jQuery en su momento para el desarrollo en Javascript.

AngularJS Logotipo
AngularJS

Para descargaros Angular, que por cierto cada versión tiene un nombre más delirante que la anterior (al momento de escribir esto estamos en la Monochromatic Rainbow, pero mi favorita es Flatulent Propulsion), podéis hacerlo desde la página del proyecto. Y desde la documentación de esa misma página he sacado el código para el primer ejemplo: un Hello World!. Pero claro, como hablamos de un framework que dota de actividad a vuestro CSS será más bien un Hello ______ (loquesea).

Empezamos, como siempre, por el marcado:


    <html ng-app>
      <head>
<script class="hiddenSpellError" type="text/javascript">// <![CDATA[
src</span>="http://code.angularjs.org/angular-1.0.2.min.js">
// ]]></script>
<script class="hiddenSpellError" type="text/javascript">// <![CDATA[
src</span>="script.js">
// ]]></script>
 </head>
 <body>
 <div ng-controller="HelloCntl">
 Your name: <input type="text" value="World" />
 <hr/>
 Hello {{name}}!
 <div>
 </body>
 </html>

Bien, como puedes ver en la cabecera cargamos como un script la librería de AngularJS (está copiado de un ejemplo antiguo, así que revisa cual es la última versión en el momento en que lo hagas) y debajo cargamos otro script con lo que hayamos programado en Javascript. Puedes ver que al div le hemos asignado un atributo llamado ng-controller. La idea de esto es definir que ese div estará dentro del ámbito del controlador que le indicamos (recuerda, estamos trabajando en un modelo MVC). Luego verás que al input le hemos puesto también un atributo, en ese caso ng-model. Lo mismo, estamos vinculando ese atributo al ámbito del modelo de datos definido, o explicado de forma más simple: el texto que hay ahí será el modelo de datos y se llamará name. Finalmente ves que al lado de Hello hemos incluido {{name}}, esencialmente la idea es que ese elemento entre llaves está relacionado en el elemento del modelo con el mismo nombre (que a su vez está relacionado con el input) ¿qué va a ocurrir? Pues el llamado two-way-data-binding: modificarás el modelo desde la vista y justo al mismo tiempo verás como los datos de la vista se modifican.

Ahora vamos con la parte del javascript, donde definimos el controlador y el modelo:

    function HelloCntl($scope) {
       $scope.name = 'World';
    }

Como puedes ver aquí definimos la función HelloCntl, que fue la que vinculamos en el marcado al div. Abajo simplemente hacemos que, por defecto, el nombre que aparezca sea World (para formar el Hello World). Si bien, si lo modificamos veremos como este cambia.

Logo AngularJS

Finalmente vamos a hablar de otro de los fuertes de AngularJS: el testeo del código. Porque AngularJS nos permite escribir test para comprobar el correcto funcionamiento, sin tener que meternos en absurdas y largas pruebas con algún complemento del navegador para depurar javascript. El teste sería este:

    it('should change the binding when user enters text', function() {
        expect(binding('name')).toEqual('World');
        input('name').enter('angular');
        expect(binding('name')).toEqual('angular');
    });

Si queréis ver en acción todo el ejemplo antes de testearlo vosotros, en la página de Angular (de donde lo he copiado yo) podréis hacerlo. Además de ver otros, como un juego del tres en raya.

En fin, ya puedes hacerte una idea de la potencia de este AngularJS, más adelante espero escribir más sobre el tema. Yo lo veo como una potente herramienta, sobre todo para el desarrollo de Apps móviles con HTML5 y CSS.

También añadiré que la principal idea es usar AngularJS con Node.js en el lado del servidor, si bien podéis desde ese lado el lenguaje que prefiráis (Ruby, PHP, Python).

Títulos de apertura con CSS3

En fin, para empezar este artículo sobre animaciones sobre títulos en CSS3 primero echad un ojo a esta página de ejemplo.Vamos a trabajar para hacer algo similar (bueno, de hecho para hacer ese ejemplo).

Ojo, yo no me he currado el css en este caso, me la encontré mientras me documentaba para otra cosa a través de este artículo, y creo que merece que lo traduzca/explique en castellano.

Años atrás un efecto como este sólo sería posible con Flash, pero ahora podemos hacerlo con la tecnología nativa de los navegadores (bueno, como siempre con la excepción de nuestro querido Internet Explorer, al menos hasta la versión 10). Cierto que en este caso también hay ayuda de un poco de Javascript, no es CSS puro, pero se trata de una simple librería que permitirá trabajar con las letras por separado sin tener que crear nosotros a mano un span para cada una. La biblioteca de JS a utilizar será Lettering.js.

Por tanto vamos primero con el marcado. Crearemos un div para envolverlo todo y meteremos dentro de él todas las frases a usar como títulos de segundo nivel (h2):

</pre>
<div class="hiddenSpellError">class="os-phrases"></div>
<pre></pre>
<h2>Sometimes it's better</h2>
<pre>
    <h2>to hide</h2></pre>
<h2>in order to</h2>
<pre>
    <h2>survive evil</h2>
    <h2>Thanatos</h2>
    <h2>This fall</h2>
    <h2>Prepare</h2>
    <h2>Refresh to replay</h2>
</div>

Ok, ya tenemos el marcado. Como dije antes, toca meter una línea de javascript (en este caso, con jQuery además)

$("#os-phrases > h2").lettering('words').children("span").lettering().children("span").lettering();

Esto lo que hará será, primero, separar cada título en palabras y cada palabra en letras, y además cada letra está metida en dos span distintos con la misma clase. En la página original podéis ver un ejemplo de cómo sería hacer eso codeándolo a mano.

Muy bien, ya tenemos la parte de marcado lista, a base tanto de código manual como de javascript para facilitar las tareas. Toca ponerse manos a la obra con el CSS.

Lo primero será lograr que nuestros títulos ocupen el 100% de la página, posicionado de forma absoluta, en mayúsculas y bien tocho:

.os-phrases h2 {
    font-family: 'Dosis', 'Lato', sans-serif;
    font-size: 70px;
    font-weight: 200;
    height: 100%;
    width: 100%;
    overflow: hidden;
    text-transform: uppercase;
    padding: 0;
    margin: 0;
    position: absolute;
    top: 0;
    left: 0;
    letter-spacing: 14px;
    text-align: center;
}

Right now! Este paso ya está listo, el título es bien tochaco. Eso sí, podría pasar que no se ajustase bien a todos los tamaños de pantalla, así que vamos a usar flexbox para solucionarlo.

.os-phrases h2,
.os-phrases h2 > span {
    height: 100%;
    /* Centrando con flexbox */
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
}

El siguiente paso es meter espaciado entre palabras:

.os-phrases h2 > span {
    margin: 0 15px;
}

Y ahora vamos a ver el por qué de los dos span para cada letra. Al primero de ellos le vamos a aplicar perspectiva:

.os-phrases h2 > span > span {
    display: inline-block;
    perspective: 1000px;
    transform-origin: 50% 50%;
}

Las letras en si serán transparentes y les meteremos una animación, que durará 5.2 segundos.

.os-phrases h2 > span > span > span {
    display: inline-block;
    color: hsla(0,0%,0%,0);
    transform-style: preserve-3d;
    transform: translate3d(0,0,0);
    animation: OpeningSequence 5.2s linear forwards;
}

Con esto ya ves la lógica del doble span.

Una vez tenemos la orientación de las letras toca definir los delays de las frases. La quinta frase, la que es el título de la «película imaginaria» tendrá un delay especial, aparecerá más lentamente. En cambio las secuencias 6 y 7 irán más rápido.

.os-phrases h2:nth-child(2) > span > span > span {
    animation-delay: 5s;
}

.os-phrases h2:nth-child(3) > span > span > span {
    animation-delay: 10s;
}

.os-phrases h2:nth-child(4) > span > span > span {
    animation-delay: 15s;
}

.os-phrases h2:nth-child(5) > span > span > span {
    font-size: 150px;
    animation-delay: 21s;
    animation-duration: 8s;
}

.os-phrases h2:nth-child(6) > span > span > span {
    animation-delay: 30s;
}

.os-phrases h2:nth-child(7) > span > span > span {
    animation-delay: 34s;
}

Antes de meternos con la última frase, que tendrá un tratamiento más especial, toca definir cómo se comportarán las letras durante la animación (es decir, cómo va evolucionando la separación, la opacidad, etc durante la animación):

@keyframes OpeningSequence {
    0% {
        text-shadow: 0 0 50px #fff;
        letter-spacing: 80px;
        opacity: 0.2;
        transform: rotateY(-90deg);
    }
    50% {
        text-shadow: 0 0 1px #fff;
        letter-spacing: 14px;
        opacity: 0.8;
        transform: rotateY(0deg);
    }
    85% {
        text-shadow: 0 0 1px #fff;
        opacity: 0.8;
        transform: rotateY(0deg) translateZ(100px);
    }
    100% {
        text-shadow: 0 0 10px #fff;
        opacity: 0;
        transform: translateZ(130px);
        pointer-events: none;
    }
}

Ok, con esto ya tenemos todo lo relativo a todas las frases… menos la última, que ya dijimos que tendría un tratamiento especial, ya que la animación será distinta al resto, usando en este caso un fundido:

.os-phrases h2:nth-child(8) > span > span > span {
    font-size: 30px;
    animation: FadeIn 4s linear 40s forwards;
}

@keyframes FadeIn {
    0% {
        opacity: 0;
        text-shadow: 0 0 50px #fff;
    }
    100% {
        opacity: 0.8;
        text-shadow: 0 0 1px #fff;
    }
}

Y ahora pensarás «¿No había algunas palabras destacadas?». Y tienes razón, algunas estaban en negrita. Pero tranqui, eso lo arreglamos con esta última línea, donde aplicamos la negrita a ciertas palabras concretas:

.os-phrases h2:first-child .word3,
.os-phrases h2:nth-child(2) .word2,
.os-phrases h2:nth-child(4) .word2 {
    font-weight: 600;
}

En fin, ya ves que es un pelín tocho el código pero que tampoco come a nadie, y se logra un resultado muy espectacular. Si no os apetece copiar, el código original está en este enlace.

A la hora de terminar este artículo, la web original estaba en remodelación. Pero echadle un ojo por si acaso porque cuando vuelva a estar activa tiene muchos trucos de diseño web muy interesantes, como todos los que ha colgado la creadora de este efecto Mary Lou.

Crear un botón Pin it para compartir en Pinterest desde tu web

Cierto es que Pinterest ha sacado un complemento para el navegador que agrega un botón Pin-it a tu Firefox o Chrome, pero con todo es recomendable agregar un botón de compartir en Pinterest a tu web. Puede que en España no sea todavía muy popular esta red social (y desconozco las estadísticas para América Latina)  pero para sitios de fotografía y moda se ha vuelto indispensable.

Como en todas las redes sociales, crear el botón es fácil (a ellos les interesa facilitarte el trabajo): Tan sólo tienes que irte al generador de widgets de Pinterest y crearlo desde allí.

Para el botón de Pin It verás que se te ofrecen 3 opciones: One Image, Any Image o Image Hover. La primera opción es para crear el botón sólo para una imagen concreta, de ahí el formulario donde se te pedirá el url de la imagen y que elijas donde ubicar un contador. Debes copiar tanto la notación HTML como el script que te da y ubicarlo donde te sea más cómodo.

Boton Pin it
Pin it!!!

La opcion Any Image te permitirá crear un botón para insertar cualquier imagen de la página en que esté ubiado en Pinterest (similar al que hay implementado a día de hoy en los productos de la web de la marca Mango, por ejemplo). Tienes que pegar un HTML en tu código para que genere el botón y un script.

Finalmente Image Hover hará que sobre cada imagen de la página en que ubiques el script aparezca un botón de compartir en Pinterest al sobrevolarla con el ratón. En este caso no habrá HTML y sólo tendrás que copiar el script.

Y así, de forma sencilla tienes el botón insertado en tu web. Para insertar el botón Pin It en un blog de Blogger podéis seguir esta guía.