2011/03/31

Minimización de fuentes I. Introducción.

A la hora de optimizar la carga de una página web, los dos puntos principales a optimizar son reducir la cantidad de bytes transferidos y reducir el número de peticiones hechas al servidor. Para el segundo punto ya hemos visto cómo hacerlo para las imágenes usando sprites, pero hay un modo de hacer ambas cosas pero aplicado a javascripts y CSSs (y también el HTML como hace por ejemplo Google), la minimización.

Aparte de estas dos ventajas, se tendrían otras secundarias como por ejemplo la ofuscación de código y aumento de la velocidad de parseo y ejecución de javascripts, pero todos los avances en un sentido tienen su contrapartida en otro, el mantenimiento.

Hay varias herramientas para la minimización, siendo las más importantes
Salvo uglify y la versión online del closure compiler de google tienen la pega que para sacarle el rendimiento real, hay que pelearse con unos ficheros de configuración bastante complejos que hacen que la pereza de ponerse a minimizar código sea mayor aún y al final por dejadez y vagancia nunca se hagan. Un ejemplo de ello es el XML de configuración del closure compiler para la generación del paquete minimizado de jQueyUI https://github.com/jquery/jquery-ui/blob/master/build/build.xml.

Una vez que se ha minimizado el código javascript y los CSSs, se hace imposible la modificación de los fuentes porque los javascripts se convierten en un galimatías incomprensible y los CSSs en un chorizo interminable de propiedades, lo que obliga a aplicar la minimización solamente al producto final y todos sabemos que todo desarrollo informático es como las obras de arte, que no se terminan, se "abandonan"... siempre queda algo por hacer o mejorar.

Bueno, pues hay una alternativa usando PHP que permite una tasa razonable de minimización y fácil mantenimiento, pero eso, en la segunda parte... mañana, más.

2011/03/30

Control de buffer con PHP

Hola, para controlar la salida del buffer de PHP, hay una serie de funciones para manejarlo. Como una imagen vale más que 1000 palabras, un ejemplo:
<?php
  // Comienzo del almacenamiento en el buffer
  ob_start();
  // Toda la salida se escribe en el BUFFER, OJO!
  echo 'Print to the screen!!!';
  // Si se quiere consultar qué se ha sacado en el buffer hasta el momento
  $getContent = ob_get_contents();
  // Fin del almacenamiento en el buffer
  ob_end_clean();
  // Procesar el contenido almacenado en el buffer mediante $getContent. Se puede cambiar todo ya que realmente aún no se ha escrito en la salida.
  // Se vuelca el buffer en la salida mediante $getContent
  echo 'Ahora: ' . $getContent;
?>

2011/03/29

Detección de clientes móviles mediante javascript, PHP y .htaccess

Recopilo las forma de detectar navegadores de dispositivos móviles mediante distintas tecnologías:

Javascript
var ua = navigator.userAgent.toLowerCase();
var isAndroid = ua.indexOf("android") > -1; // && ua.indexOf("mobile");
var isiPhone = ua.indexOf("iphone") > -1;
var isiPod = ua.indexOf("ipod") > -1;
var isiPad = ua.indexOf("ipad") > -1;
// For use within iPad developer UIWebView
if(!isiPad) isiPad = /iPad/i.test(ua) || /iPhone OS 3_1_2/i.test(ua) || /iPhone OS 3_2_2/i.test(ua);
var isWPx = ua.indexOf("windows phone") > -1;
PHP
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
$isAndroid = stripos($ua, 'android') !== false; // && stripos($ua,'mobile') !== false
$isiPhone = stripos($ua, 'iphone') !== false;
$isiPod = stripos($ua, 'ipod') !== false;
$isiPad = stripos($ua, 'ipad') !== false;
$isWPx = stripos($ua, 'windows phone') !== false;
.htaccess
RewriteCond %{HTTP_USER_AGENT} ^.*Android.*$ RewriteRule ^(.*)$ http://android.site.com [R=301] RewriteCond %{HTTP_USER_AGENT} ^.*iPhone.*$ RewriteRule ^(.*)$ http://iphone.site.com [R=301] RewriteCond %{HTTP_USER_AGENT} ^.*iPod.*$ RewriteRule ^(.*)$ http://ipod.site.com [R=301] RewriteCond %{HTTP_USER_AGENT} ^.*iPad.*$ RewriteRule ^(.*)$ http://ipad.site.com [R=301]RewriteCond %{HTTP_USER_AGENT} ^.*Windows Phone.*$RewriteRule ^(.*)$ http://wpx.site.com [R=301]
HTML

<!--[if IEMobile]>
    Explorer mobile en Windows Phone 7
<![endif]-->
<![if !IEMobile]>
    Otros
<![endif]>

2011/03/28

Contenteditable

Esta propiedad definida en HTML5 permite modificar los contenidos de las siguientes etiquetas:
<a> <acronym> <address> <b> <bdo> <big> <blockquote> <body> <button> <center> <cite> <code> <custom> <dd> <del> <dfn> <dir> <div> <dl> <dt> <em> <fieldset> <font> <form> <hn> <i> <input type="button"> <inputtype="password"> <input type="radio"> <input type="reset"> <inputtype="submit"> <input type="text"> <ins> <isindex> <kbd> <label> <legend> <li> <listing> <marquee> <menu> <nobr> <object> <ol> <p> <pre> <q> <rt> <ruby> <s> <samp> <small> <span> <strike> <strong> <sub> <sup> <textarea> <tt> <u> <ul> <var> <xmp>
La sintaxis es:
<element contenteditable="value" > . . . </element>
Los valores para "value" son

  • inherit - Herede la posibilidad de ser editado dede el padre. Es el valor por defecto
  • false - No se puede editar
  • true - Sí se puede editar

Para poder leer el valor del elemento, hay que recurrir a la propiedad innerText desde javascript. No todo iba a ser un camino de rosas, verdad?.

Un ejemplito tontorrón. Básicamente lo que hace es que al hacer click en el <p> se pueda escribir su contenido.
<HTML>
<head><Title>Example For contenteditable</Title></head>
<BODY>
    <p contenteditable="true">contenteditable</p>
</BODY>
</HTML>

2011/03/27

Citius, altius, fortius

Para los que están haciendo páginas web y que aún no usan Chrome... básicamente están perdiendo el tiempo.
A día de hoy, el navegador más avanzado para desarrollo web es el chrome de lejos... muy de lejos y no solo porque vaya más rápido, que sea más compatible con los estándares, que se actualiza más a menudo o simplemente por diseño minimalista.

Lo único que se puede parecer a la experiencia de desarrollar y depurar páginas web podría ser firefox+firebug, con la pega de que el firebug se come el ordenador después de un par de horas funcionando... pero eso son detalles.

El poder modificar el CSS, Javascript, HTML, etc. en vivo sobre la propia página no tiene precio, en serio. Si a alguien le ha picado el gusanillo, puede ver la documentación de las herramientas de desarrollo de chrome en:
http://code.google.com/intl/es-ES/chrome/devtools/docs/overview.html
Y para una video guía que resume todo, en
o en la sección de vídeos de chrome
http://code.google.com/intl/es-ES/chrome/devtools/docs/videos.html

2011/03/25

2011/03/24

Usando imágenes para dar estilo a los elementos IV. Hacks de background-image

Aunque ya hemos visto una forma óptima de tratar con sprites, aún se le puede sacar algo de jugo para determinados casos. Las secciones de este artículo se van a aplantear como si existieran las propiedades CSS de las que se habla.

Emulando background-crop.
Básicamente, es lo que hemos estado haciendo en el capítulo III de esta serie de posts. Podría ponerse simplificado, pero se perdería la posibilidad de hacerlos cross-browser. Merece la pena repasar el artículo.
Hay tímidos avances en incorporar esta funcionalidad como la de firefox con -moz-image-rect y la inclusión de image slices en el borrador para imágenes de CSS3 por lo que dentro de poco debería ser una característica de base en vez de una rareza.


Emulando multiple-background (CSS2.1)
Para navegadores medianamente actuales (es decir, IE8+) se puede aplicar esta técnica. La base de todo está en cómo se componen las capas de los pseudo-elementos :before y :after en un elemento HTML. Se puede ver cómo en el diagrama de la izquierda.

Así, se podría tener un fondo para el elemento, un fondo en el :before y otro en el :after, teniendo encima de todo el contenido del elemento, todo en ese orden de "abajo" a "arriba".

Un ejemplo de ésto funcionando creando un efecto además de tipo parallax, es el que se puede ver en el siguiente CSS.

Se supone que se tiene un elemento llamado #contenedor y dentro de él se va a situar before y after una imagen, cada una con su propio fondo.
#contenedor {
   position:relative;
   z-index:1; /* pone el orden correcto */
   background:#d3ff99 url(fondo-mas-profundo.png) -10% 0 repeat-x; /* el fondo de atrás del todo */
   min-width:200px; min-height:200px; padding:120px 200px 50px; /* esto es estético */
}
/* Estilos comunes de :before y :after */
#contenedor:before,
#contenedor:after {
   position:absolute;
   z-index:-1; /* pone el orden correcto */
   top:0; left:0; right:0; bottom:0;
   padding-top:100px; /* estético */
}
/* Estilos específicos de :before y :after */
#contenedor:before {
   content:url(imagen-1.png); /* El contenido es una imagen en este caso */
   padding-left:3%; text-align:left;
   background:transparent url(fondo-intermedio.png) 300% 0 repeat-x;
}
#contenedor:after {
   content:url(imagen-2.png); /* El contenido es una imagen en este caso */
   padding-right:3%;
   text-align:right;
   background:transparent url(fondo-primer-plano.png) 70% 0 repeat-x;
}
 
Emulando background-transform.
Combinando pseudo-elementos mediante :before con transformaciones transform de CSS3, se pueden hacer cosas como por ejemplo, rotar el icono de un acordeón al ser pulsado.

Evidentemente, IE 6, 7 y 8 no están invitados a esta fiesta aunque se podría currar algo con filtros, cosa que no voy a hacer en este momento.
.accordion a:before {
   content:"";
   float:left;
   width:16px;
   height:16px;
   margin:0 5px 0 0;
   background:url(sprite.png) no-repeat 0 0;
}
.accordion.open a:before {
   -webkit-transform:rotate(90deg);
   -moz-transform:rotate(90deg);
   -ms-transform:rotate(90deg);
   -o-transform:rotate(90deg);
   transform:rotate(90deg);
}
Emulando background-transform-mirror
Este es una tontería, pero el resultado es algo que más de una vez he tenido que hacer y que he tenido que recurrir a photoshop y similares.

En vez de rotar el elemento, se hace un transform: scaleX(-1) para que las sombras queden bien.

Para simular que se ha presionado el botón y que las sombras queden bien, se hace la transformación de escalado tanto en el eje X como en el Y con transform: scale(-1,-1)

El código CSS del ejemplo sería.

.prev a:before,
.next a:before {
   content:"";
   float:left;
   width:16px; height:16px; margin:0 5px 0 0; /* tamaños */
   background:url(sprite.png) no-repeat 0 0;
}
.next a:before {
   float:right; /* Lo coloca a la derecha */
   margin:0 0 0 5px;
   -webkit-transform:scaleX(-1);
   -moz-transform:scaleX(-1);
   -ms-transform:scaleX(-1);
   -o-transform:scaleX(-1);
   transform:scaleX(-1);
}

Como os habréis imaginado el IE8 (y anteriores) no están invitados a la fiesta.

Emulando background-position
CSS 2.1 limita el uso de background-position a desplazamientos desde los lados izquierdo y superior de la imagen, pero sin embargo es posible emular el posicionamiento a partir de los border derecho e inferior aplicando el fondo a un pseudo-elemento y luego desplazando dicho pseudo-elemento.

La base es la misma que en la técnica de multiple-background y el CSS quedaría así, considerando que es el elemento #contenedor al que se le quiere colocar el fondo a partir de la derecha o parte inferior:


#contenedor {
   position:relative; /* necesario */
   z-index:1; /* necesario para ordenar las cosas correctamente */
}
#contenedor:before {
   content:"";
   position:absolute; /* necesario */
   z-index:-1;  /* necesario para ordenar las cosas correctamente */
   bottom:10px; /* Colocando respecto a la parte inferior */
   right:10px;  /* Colocando respecto a la parte derecha  */
   width:500px; height:300px;
   background:url(image.jpg);
}

Emulando background-opacity
Desarrollando un poco las técnicas que hemos estado viendo antes, se puede aplicar opacity al background de uno de los dos pseudo-elementos :before o :after de modo que se hagan transparencias con el background del propio elemento.


#contenedor {
   position:relative;
   z-index:1;
}
#contenedor:before {
   content:"";
   position:absolute;
   z-index:-1;
   top:0; bottom:0; left:0; right:0;
   background:url(image.jpg);
   opacity:0.7;
}
Una vuelta de tuerca a todo esto es la de que el :before tuviera una transparencia de por ejemplo .75 y simplemente cambiando el z-index de -1 a 1, no solo hacemos que se vea el contenido de forma translúcida, si no que además no se permitiría la selección por ratón!. He oido por ahí "selection-disabled"?. Lástima que ya exista, pero seguro que algún uso se le puede dar...
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
user-select: none;

2011/03/23

Usando imágenes para dar estilo a los elementos III. Usando imágenes

En el primer capítulo, hemos visto cómo usar sprites para crear estilos más atractivos, pero los sprites tienen pegas como son que se pueden ver fragmentos de otros sprites si nos nos andamos con cuidado y que hay que organizar muy bien la imagen de los sprites dejando mucho espacio alrededor y mucho espacio = imagen más grande = más lento. Si pudiéramos recortar la imagen de los sprites y quedarnos con lo que nos interesa... espera... que sí que se puede!

Se nos va a complicar un poco el CSS, pero podemos definir la imagen de los sprites como nos de la gana mezclando sprites de cualquier tamaño en cualquier sitio sin preocuparnos de que se vea lo que no debe y optimizando la imagen para que no queden espacios vacíos.

El secreto es usar la propiedad CSS2 "clip" que permite definir un rectángulo de recorte de la imagen que se diga.

Los sprites tradicionales, tendríamos que hacerlos así:

#first:before {
    content: "";
    float: left;
    width: 15px; height: 15px;
    margin: 4px 5px 0 0;
    background: url(sprite.png) -15px 0;
}
Pero con la nueva técnica se podrían hacer de la siguiente manera:
#second {
  position: relative;
  padding-left: 20px;
  background-image: expression(this.runtimeStyle.backgroundImage="none",this.innerHTML = '<img alt="" src="sprite.png">'+this.innerHTML);
}
#second:before,
#second img {
  content: url(sprite.png);
  position: absolute;
  top: 3px;
  clip: rect(0 30px 15px 15px);
  left: -15px; /* to offset the clip value */
  _left: -35px; /* some massaging for IE 6 */
}
Vale, es más compleja, pero hace lo que debe. Vamos a verlo en más detalle.
Lo primero, es definir el estilo base, dejando espacio a la izquierda para colocar el icono con el "padding-left: 20px". El "position:relative" es necesario para hacer que funcione la segunda parte.

Después, se aplica la pseudo-clase ":before" que es donde se muestra el sprite en sí con la propiedad "content" que cargaría tooooda la imagen. Acto seguido, la recorta con "clip" y la coloca en el (0,0) con el "top" y el "left"... es decir, en "top" está la -Y del origen vertical del sprite (en el ejemplo no es necesario) y "left" el -X del origen horizontal del sprite ya que la imagen tiene los sprites en "horizontal". El "position: absolute" es necesario porque "clip" solo funciona con coordenadas absolutas.

Vale, ahora vienen las chapuzas para que IE6/7 funcionen como deberían haberlo hecho. la primera es la expression que inyecta el código de la <img> de los sprites, la segunda, es aplicarle el estilo a la imagen del elemento con "#second img" y la tercera el usar el css hack "_left" que no entiende nadie salvo IE6 para colocar el sprite donde debe.

Ventajas
  1. Permite optimizar la imagen de los sprites y organizarla como nos de la gana.
  2. Permite imprimir correctamente. Al no ser un background, se va a imprimir. En el caso de la técnica clásica de sprites, si se imprime la página, los sprites no se imprimirían salvo que se especifique "imprimir background" con el riesgo de que se imprima TODO.
  3. Accesible. Compatible con todos los métodos de accesibilidad y visualización de alto contraste.
  4. Funciona en IE 6, 7, 8 y 9. Sorprendente, verdad?
Inconvenientes. Hay que escribir más... no es realmente un inconveniente, pero todos hemos nacido cansados, verdad?

2011/03/22

ZEN - HTML5-CSS3 Audio Player

ZEN - HTML5-CSS3 Audio Player

Reproductor minimalista hecho con la etiqueta
Como usa tanto HTML5 como CSS3, abstenerse de entrar con navegadores vetustos como el IE8.
En Chrome se ve impresionante y en Firefox pasable.

A mí me ha encantado y mirando bajo el capó hay mucho que aprender.

Usando imágenes para dar estilo a los elementos II. Usando símbolos

Segunda entrada de la serie de estilado de elementos.

Hay veces que no es necesario usar imágenes para dar estilo a elementos como por ejemplo en listas de elementos a modo de bullets (suponiendo que no fuera aplicable o no quisiéramos usar la imagen de bullet con list-style-image: url(imagen.gif) ). Se podrían usar caracteres especiales para conseguir el mismo efecto ahorrándonos peticiones HTTP para cargar una imagen que al fin y al cabo haría lo mismo además de que al ser caracteres en vez de imágenes sería más accesible.

Para los ejemplos, se van a colocar los símbolos de la baraja francesa (picas, corazones, diamantes y tréboles) delante de un elemento del siguiente ejemplo:

<ul class="glyphs">
    <li class="one">Picas</li>
    <li class="two">Tréboles</li>
    <li class="three red">Corazones rojos</li>
    <li class="four red">Diamantes rojos</li>
</ul>
El CSS para conseguir ésto es:
.glyphs { list-style-type: none; }
  .glyphs li:before,
  .glyphs b { display: inline-block; width: 1.5em; font-size: 1.5em; text-align: center; }
    .one, .one:before {
      content: "\2660"; /* Picas */
      background-image: expression(this.runtimeStyle.backgroundImage="none",this.innerHTML = '<b>&spades;</b>'+this.innerHTML);
    }
    .two, .two:before {
      content: "\2663"; /* Tréboles */
      background-image: expression(this.runtimeStyle.backgroundImage="none",this.innerHTML = '<b>&clubs;</b>'+this.innerHTML);
    }
    .three, .three:before {
      content: "\2665"; /* Corazones */
      background-image: expression(this.runtimeStyle.backgroundImage="none",this.innerHTML = '<b>&hearts;</b>'+this.innerHTML);
    }
    .four, .four:before {
      content: "\2666"; /* Diamantes */
      background-image: expression(this.runtimeStyle.backgroundImage="none",this.innerHTML = '<b>&diams;</b>'+this.innerHTML);
    }
    .red b, .red:before { color: red; }
Ventajas de este modo de representación:
  1. Rendimiento. 0 peticiones HTTP = página más rápida.
  2. Usabilidad. El glifo de texto se escala de acuerdo con el tamaño de la fuente de la página.
  3. Mantenimiento. Al no usarse sprites, no hay que mantener ni imágenes ni posiciones. Fire-and-forget.
  4. Accesibilidad. WYSIYG. Lo que ves es lo que hay.
Cómo funciona?
La base es hacer que la propiedad del before de los li de la lista sea "display: inline-block; width: 1.5em;" con lo que indica tanto cómo se va a representar como que tiene ancho fijo pero proporcional al tamaño de la fuente, lo que lo hace escalable. 

El secreto está en el uso de la propiedad CSS2 "content" y la pseudo-clase ":before". Con ella, se puede inyectar un contenido delante (a la izquierda hablando en llano) del contenido del elemento. Pero hay una pega. :before y el resto de las pseudo-clases y pseudo-elementos no funcionan en la misma mierda de siempre, Internet Explorer antes de la 8, pero hay una solución, usar expresiones CSS.

Las expresiones CSS, son mortales para el rendimiento de una página como hemos visto en entregas anteriores, pero son el único modo de funcionamiento para IE6/7 por lo que si queremos que nuestro CSS en este caso se vea igual en todos los navegadores hace que no sea algo opcional. En este caso,  la expresión modifica el fuente de la página de modo que cuando se cumpla la regla CSS, se inyecte un código HTML "<b>glifo</b>" justo delante. Como hemos definido reglas para los <b> de los <li> de la lista, eso hará que podamos aplicarle además otras reglas a los elementos <b>, como es la regla "red" en el ejemplo.

Hay que tener en cuenta que para la propiedad content, hay que inyectar texto en formato unicode, mientras en que en las expresiones al inyectar HTML, se pueden usar códigos HTML normales.

Como referencia, en esta página se puede obtener el código unicode de los 65536 caracteres que se pueden definir.

2011/03/21

Detección del navegador con PHP

A veces nos hace falta saber qué navegador y versión está visualizando una página antes de nada en el propio PHP... es decir... si es un navegador de verdad o una "cosa" como IE6.

Un código sencillo para hacerlo sería el siguiente, dejando la variable $IE = 1 si la versión de Internet Explorer es <= que la 6.

<?php
// Identifica el navegador usado
$IE = 0;
$agent = trim($_SERVER['HTTP_USER_AGENT']);
if ((preg_match('|MSIE ([0-9.]+)|', $agent, $version)) || (preg_match('|Internet Explorer/([0-9.]+)|', $agent, $version)))
$IE = 1; // INTERNET EXPLORER
// Versiones modernas de Internet Explorer funcionan como debe
if ($version && $version[1] > 6) $IE = 0;
?>

Usando imágenes para dar estilo a los elementos I. Sprites

A todos nos gusta poner los elementos de las páginas web "de domingo" y para ello usamos un montón de imágenes que generalmente terminan redundando en un mayor número de peticiones HTTP, a.k.a. cargas más lentas.

Bueno, pues voy a iniciar una serie de artículos sobre cómo conseguir los mismos efectos reduciendo el número de peticiones buscando siempre la simplicidad, entendiendo por simple en estos casos, priorizar el uso de CSS antes que usar javascript porque como regla general, los CSSs de procesan antes que los JSs.

Empecemos con la técnica más simple, los sprites. Para los despistados, un sprite es una imagen que se repite a lo largo de un programa y que se usa como representación gráfica. En corto, se usa a menudo y además es información gráfica.

Los sprites son el abc de la optimización de los contenidos de cualquier web. Es decir, vamos a buscar una manera de almacenar información gráfica en un repositorio de información (en nuestro caso, en una imagen mucho más grande) para cargarla una única vez y desde ahí usarla para poblar todos los contenidos gráficos.

Por qué se hace esto? pues sencillo... porque generalmente se gasta como mucho un 25% del tiempo cargando el HTML y un 75% o más cargando otros contenidos, y los más pesados, suelen ser las imágenes por lo que si reducimos tanto el tamaño de las imágenes como el número de éstas, estamos aumentando la velocidad y el tráfico de todo el sitio web. Como regla general, 1 petición de una imagen grande se hace más rápido y se transfieren menos datos que esa misma imagen repartida en 10 peticiones de sus distintas partes.

Empecemos. Tradicionalmente, todos hemos usado los CSS de esta manera:

#nav li a {background:none no-repeat left center}
#nav li a.item1 {background-image:url('../img/image1.gif')}
#nav li a:hover.item1 {background-image:url('../img/image1_over.gif')}
#nav li a.item2 {background-image:url('../img/image2.gif')}
#nav li a:hover.item2 {background-image:url('../img/image2_over.gif')}

Esto equivale en este caso a 4 peticiones HTTP, más la transferencia de 4 ficheros.

El quid de la cuestión para crear reglas CSS que usen sprites, es la propiedad background-position, de modo que mediante el posicionamiento, se desplaza el background hacia arriba o abajo en función de la imagen que se quiera mostrar con números negativos para los píxeles para ver la imagen que queda más abajo en el sprite:

#nav li a {background-image:url('../img/image_nav.gif')}
#nav li a.item1 {background-position:0px 0px}
#nav li a:hover.item1 {background-position:0px -72px}
#nav li a.item2 {background-position:0px -143px;}
#nav li a:hover.item2 {background-position:0px -215px;}

Los sprites tal cual, tienen un problema... los "cachos" en que se dividen, tienen que ser suficientemente amplios para que al desplazar el fondo, no se vean artefactos al mostrarse porciones de los otros iconos que contiene el sprite, además, generalmente debe tener cada fragmento el mismo alto que el resto de sus hermanos. Vamos a ver... en un fichero pueden mezclarse sprites de cualquier tamaño, pero es recomendable organizarlo de modo que los sprites parecidos como por ejemplo las imágenes de fondo de las pestañas estén juntas una detrás de otras para no volverse loco a la hora de poner las coordenadas en el CSS. Para el manteniendo la imagen de los sprites, los nuevos sprites se añadirían a la imagen por "abajo".

Ahora, la pregunta del millón... ya tengo una web y quiero pasarla a sprites... pues hay herramientas que ayudan a ésto. Una de las mejores es la web SpriteMe, que analiza un URL que se le pasa y genera un sprite con todas las imágenes que detecta en él. Lo mejor del sitio éste, es que tiene una opción de exportación del CSS para el sprite generado que dice en qué entradas del CSS original hay que reemplazar el url(xxx)  por url(yyy) + background-position + coordenadas del sprite. Realmente útil.

2011/03/19

Bucles rápidos en javascript

Hola, hoy os voy a contar varias maneras de hacer lo mismo, pero mejor. Bucles en javascript.
Todos estamos acostumbrados a hacerlos así:
for(i = 0; i < Obj.length; i++){}
Pero esa no es la manera mejor. Se puede mejorar haciendo lo siguiente que es un 20% más rápido
for(i = 0, ii = Obj.length; i < ii; i++){}
Pero espera, que aún puede ser mejor. Un 50% más rápido
for(i = 0, ii = Obj.length; ii--;){}

Otras alternativas de cómo hacer bucles...
for(var i = -1; ++i < Obj.length; ){}
Una variación algo más rápida
for(var i = -1, ii = Obj.length; ++i < ii; ){}

La velocidad de cada uno se puede ver en la siguiente gráfica:

Y de premio, una manera mejor de recorrer un array de objetos
while (p = o[i++]) { ... }

Como muy bien ha señalado Sócrates en su comentario, no queda claro en la gráfica qué bucle es cuál, por lo que lo mejor es mirar el jsperf que compara todas las variantes con datos actuales:

http://jsperf.com/loops/146

2011/03/18

CSS y el modelo de caja

Algo realmente frustrante para mí es cuando le digo a un elemento web que mida por ejemplo 200px, cargo la página, lo mido y resulta que al final da que mide 202 píxeles!!!... qué pasa????.... más frustrante es aún el hecho de que ese miemo elemento, cuando se mide en IE, mide 200, pero en el resto 202.

El quid de la cuestión, es que ese elemento tiene la propiedad border a 1px y que según el navegador y la versión, se considera que el borde está contado dentro del tamaño del elemento o no.

Bueno, pues aquí llega CSS3 al rescate (mareando aún más) con la definición del modelo de caja. Resulta que el consorcio W3C dice que el borde NO forma parte del tamaño del elemento, es decir, está por fuera, lo que hace que desde el momento que usas elementos con borde, se te va a freir espárragos cualquier diseño pixel perfect... o por ejemplo si usas 960gs con formularios y a algún input le pones propiedad de border para que quede más bonito... los diseños se descuadran, empiezas a hacer guarrerías para que quede como debe, etc...

Todo este cuadro, queda más claro explicado con un dibujo:

Como decía antes, con CSS3 al menos tenemos la opción de elegir cuál es el modelo de caja que nos va a descuadrar nuestro diseño (ya que por lo que he visto, según el que uses te arregla unas cosas y te descuadra otras y viceversa).

No hay más que asignar la propiedad box-sizing para que quede todo claro. Aquí están los dos posibles valores:

box-sizing: border-box;
box-sizing: content-box;
Como es lógico, no todo puede ser tan sencillo. Resulta que cada navegador interpreta la propiedad CSS a su manera, así que para que funcione, hay que añadir el tag específico de cada familia de navegador:
/* W3C */
box-sizing: border-box
box-sizing: content-box
/* for Safari & Google Chrome */
-webkit-box-sizing: border-box;
-webkit-box-sizing: content-box;
/* Mozilla */
-moz-box-sizing: border-box;
-moz-box-sizing: content-box;
-moz-box-sizing: padding-box; /* Este es específico de mozilla */
/* IE8 */
-ms-box-sizing: border-box;
-ms-box-sizing: content-box;
Y diréis... para qué sirve? pues fácil. A mí me ha salvado la vida con el estilado de formularios de modo que usando el modelo de border-box sobre los elementos de formularios, puedo usar bordes y 960gs sin morir en el intento simplemente añadiendo esta regla al CSS:
form, fieldset, input, select, textarea { box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; -ms-box-sizing: border-box; }

2011/03/17

El que rompe, paga y se lleva los trozos, a.k.a. limpiando el código en tiempo de ejecución.

Lo que sobra se tira... así que se debería hacer limpieza de las siguientes cosas:

1.- Propiedades de objeto que ya no se usan
var myApp = {
    propiedad: <algoenorme>;
};
// ...
delete myApp.propiedad;
2.- Elementos DOM que no se necesitan. Ojo, remove no es lo mismo que delete!
var el = $('myDiv'); // jQuery, para los despistados
...
// El elemento se quita del padre pero sigue estando ahí
el.parentNode.removeChild(el);
// Quitamos el elemento y lo borramos de la memoria
delete el.parentNode.removeChild(el); 

2011/03/16

Acceso al DOM mediante javascript por punteros

Dando vueltas al acceso a la información de DOM, resulta que si almacenamos en una variable local el puntero a la función que se encarga de de realizar la consulta al DOM, se reduce mucho el tiempo en un orden de 4 veces más rápido!

Ejemplo:

function foo() {
    var get = document.getElementsBtTagName;
    for (var i = 0; i < 100000; i++) {
        get('head');
    }
}
foo(); // llamada a la función
Y ésta, para nota...
... cualquier cambio que se haga por ejemplo al innerHTML de un elemento está "prohibido" hacerlo dentro de un bucle... lo que hay que hacer es realizar las modificaciones sobre una variable local que contendrá la cadena de texto y una vez se termine el bucle, se asigna el resultado. Hay que recordar que hasta no se termina la ejecución de la llamada al javascript no se realizan cambios en la UI.

2011/03/15

Canvas para todos

Se puede pensar que el HTML5 es para navegadores "modernos" como está empeñado en vendernos la moto Microsoft con su versión del Internet Explorer 9, que implementa como novedad lo que el resto ya tiene desde hace varios años (y por varios, me refiero a más de 2 y de 3), bueno, pues no es del todo cierto, al menos para el objeto canvas.

Internet Explorer (includo "eso" llamado IE6) también puede disfrutar del objeto canvas canvas de HTML5 incluyendo un javascript de un proyecto llamado ExCanvas.

Para incluirlo, hay que hacer:
  1. Descargar e instalar los fuentes desde http://code.google.com/p/explorercanvas/
  2. Añadir el script para que se cargue en versiones de IE anteriores a la 9: <!--[if lt IE 9]><script type="text/javascript" src="/js/excanvas.js"></script><![endif]-->
  3. Fire-and-forget.
Limitaciones de ExCanvas
  • Los gradientes solo pueden ser lineales. Los gradientes radiales no están soportados.
  • Los patrones (pattern) deben repetirse en ambas direcciones.
  • Las regiones de recorte (clipping) no están soportadas.
  • El escalado no uniforme no escala correctamente los trazos (strokes).
  • Es lento, más teniendo en cuenta que el motor de javascript del IE es una tortuga. Para cosas como dibujar líneas o transformar imágenes está bien, pero cosas complejas como animaciones le cuesta.

2011/03/14

6 maneras de cargar imágenes

Que eres de los que siempre usa <img src="imagen"/> y no sabes que existen varias maneras de hacerlo con diversas ventajas?... pues sigue leyendo.

Siempre desde el punto de vista de reducción de peticiones y velocidad vamos a ver 6 maneras de asignar las imágenes reduciendo la penalización en la carga de la imagen por la apertura de conexiones para mostrarlas.

1.- La clásica
0 optimizaciones. Me aburroooo... pero si no, dime cómo llego a tener 6 maneras, ein?
<img src="/images/meaburro.gif"/>
2.- Carga por javascript
Esta es la más sencilla. Se crea el tag en el html y luego se asigna por código. El ejemplo que he puesto es tonto, pero este método es realmente bueno en mejorar la respuesta realizando lazy-load de las imágenes cuando haga falta de la misma manera que las peticiones Ajax agilizan el funcionamiento de la página distribuyendo las peticiones a lo largo del tiempo.
<img src="" id="imagen">
<script>
    document.getElementById("imagen").src = "/images/imagen.png";
    $("#imagen").attr("src", "/images/imagen.png"); // versión jQuery
</script>
3.- Inclusión de la imagen en el CSS
Dado que el CSS se cachea, una manera de reducir drásticamente las peticiones HTTP para la carga de imágenes es que directamente NO se hagan peticiones incluyendo el contenido de las imágenes en el CSS. Para ello hay que hacerlo en 2 pasos

1 Generación a partir de un fichero con PHP (por ejemplo);
php -r "echo base64_encode(file_get_contents('imagen.png'));"
2 Inclusión del resultado en el CSS (en el ejemplo se asigna la imagen de fondo):
#elemento: background-image: url("data:image/png;base64, ...imagen...");
4.- Inclusión de imágenes en el HTML en el propio tag <img>
Si no te gusta guarrear el CSS con cosas raras, puedes optar por guarrear el HTML con cosas raras. Solo un aviso, si tu página no se cachea, estás penalizando la transferencia!. Lo primero igual que antes es generar el base64 de la imagen, acto seguido se añade el código al tag <img>. Quizás no le veas el punto, pero sigue leyendo.
<img src="data:image/png;base64,...imagen..."/>
5.- Asignación directa al src del <img> sin cargas intermedias.
Podíras decir que el método 3 es una chorrada, pero donde realmente brilla es cuando se combina con javascript. Podemos decir que es una evolución del primer método y es realmente útil para mostrar imágenes generadas como por ejemplo las que provienen de un renderizador o un raytracer (sí, ambos existen y están hechos con javascript)
<img src="" id="imagen">
<script>
    document.getElementById("imagen").src = "data:image/png;base64,...imagen...";
</script>
Ahora llega el problema de los "peros" para los métodos 3 y 4 (y posiblemente el 5 y el 6) y como siempre vienen del mismo lado... Internet Explorer para versiones anteriores a la 8. Sin embargo, se puede resolver fácilmente haciendo un par de manipulaciones muy sencillas en el CSS. Para más información para masocas que se empeñen en seguir usando IE mirad en este enlace.

6.- Canvas
Esto ya es artillería pesada de la que me gusta a mí... aquí es donde se saca el Ferrari a la calle a pasear para lucirse. Si aún no sabes lo que es el objeto canvas, o vives debajo de una piedra o te importa un carajillo de pato el tema del desarrollo web, pues ya estás tardando en enterarte. Baste decir que es el principal caballo de batalla hoy en día sobre rendimiento de navegadores ya que la mayoría (no, Internet Explorer aún no... a duras penas puede llamársele navegador) ya está acelerado por hardware dando rendimientos parecidos a aplicaciones compiladas.

Bueno, al lío. El objetivo de esta técnica es generar una imagen en el canvas y luego asignarla al <img>. Puede servir por ejemplo para aplicar una marca de agua, aplicar transformaciones a una imagen, hacer un editor de fotos online, etc...

Como no hay nada mejor que un ejemplo, ahí va (por cierto, lo he hecho con jQuery por si veis cosas "raras" como por ejemplo $('algo') ):
<script>
// Crea dinámica un elemento canvas para realizar las manipulaciones
var canvas = document.createElement('canvas'),
    ctx = canvas.getContext("2d");


// Dimensiona el canvas
canvas.width  = <ancho>;
canvas.height = <alto>;


// Realiza las operaciones sobre el cavas para navegadores con HTML5 (sí, Internet Eplorer también puede, ver Bonus!)
//... ejemplo pintando aleatoriamente píxeles en el canvas ...
var imgData = ctx.createImageData(canvas.width, canvas.height); // crea un buffer de acceso directo a los pixels del canvas
var rand = function(min, max) { return Math.floor(Math.random()*(max-min)+min); };
for (var i = 0; i < 0.9 * Math.pow(<ancho>, 2); i++) {
    var x = rand(0, canvas.width),
        y = rand(0, canvas.height),
        index = (x + y * imgData.width) * 4;


    imgData.data[index  ] = rand(0, 255); // red
    imgData.data[index+1] = rand(0, 255); // green
    imgData.data[index+2] = rand(0, 255); // blue
    imgData.data[index+3] = rand(0, 255); // alpha también aleatoria
}
ctx.putImageData(imgData, 0, 0); // aplica los cambios realizados al buffer en el canvas
//... fin ejemplo pintado aleatorio ...


// realiza la "magia" sobre la imagen de fondo de pantalla de #elemento con la función toDataURL. Para aplicarla al src, mirad el método 2
$('#elemento').css('background-image', 'url(' + canvas.toDataURL('image/png') + ')');
</script>
El resultado va en el rango de espectacular a alucinante. Libertad total para el tratamiento de imágenes.http://3nibbles.blogspot.com/2011/03/canvas-para-todos.html

    2011/03/13

    Optimización de javascript (VIII). Optimizaciones javascript para acceso al DOM

    Si hay algún ermitaño que no sabe qué es el DOM, ya puede ir a hacerse el hara-kiri con una cuchara oxidada a otra parte.

    Bueno, para optimizar el código de acceso al DOM de una página web, hay unos cuantos consejillos que ayudarán a arañar milésimas a la ejecución de código.

    Lo primero que hay que saber es que todas las colecciones DOM están vivas (las devueltas por llamadas del tipo document.images, document.forms, getElementsByTagName(), getElementsByClassName()), de modo que se actualizan cuando el documento subyacente cambia.

    Es decir, el siguiente código es un bucle infinito!
    var divs = document.getElementsByTagName("div"); // aquí está la consulta a la colección DOM
    for(var i=0; i<divs.length; i++) {
    var div = document.createElement("div") ;
    document.body.appendChild(div);
    }
    Objetos HTMLCollection
    Ahora que estamos centrados en materia, hablemos de los objetos de tipo HTMLCollection, que tienen estas características:
    • Son el resultado de una consulta específica.
    • Parecen arrays, pero no lo son.
    • Tienen la propiedad length
    • La consulta SE RECONSULTA cada vez que se accede al objeto o se consulta su propiedad length. Es mucho más lento que acceder a un array normal (salvo opera y safari). Hasta 68 veces más lento!, avisados quedáis
    Recomendaciones
    • Minimizar el acceso a las propiedades.
    • Almacenar el length en una variable local si se accede frecuentemente.
    • Si se necesita acceder frecuentemente a elementos, almacenarlos en un array. El siguiente código se encarga de convertir en un objeto de tipo array un objeto de tipo HTMLCollection


    function array(items) { 
        try {
            return Array.prototype.slice.call(items);
        } catch(ex) {
            var i = 0;
            len = items.length;
            result = Array(len);
            while (i < len) 
            {
                result[i] = items[i];
                i++;
            }
        }
        return result;
    }


    Reflow
    Ya hemos hablado de ello, pero es importante tenerlo en cuenta. El reflow es el cálculo de la disposición de los elementos en una página. Se calcula cuando:
    • Se hace la carga inicial de la página
    • Se redimensiona la ventana del navegador
    • Se añaden o eliminan nodos del DOM
    • Se aplican estilos de layout
    • Se obtiene información de layout
    Fragmentos de documento (objetos DocumentFragment)
    Es una técnica de composición fuera del documento principal con lo que no se producen cambios mientras se modifica. Es un objeto parecido a el objeto document del DOM. No tiene representación visual. Se considera como un hijo del document desde el que ha sido creado. Cuando se  pasa como parámetro a una llamada .addChild(), añade todos sus hijos  en vez de solamente él mismo.

    Solo fuerzan un reflow cuando él mismo se añade y no cuando se añade un hijo al fragmento de documento. Es decir, se puede crear un nodo raíz y añadirle hijos de modo que no se fuerza el reflow hasta que no se añade el raíz al documento.

    Un ejemplo:

    // Ejemplo de DocumentFragment
    var list = document.GetElementByClassName(“items”)[0],
               fragment = document.createDocumentFragment(),
               i, item;
    for(i=0; i < 10; i++) {
            item = document.createElement(“li”);
            item.innerHTML = “Elemento #” + i;
            fragment.appendChild(item);
    }
    list.appendChild(fragment); // En este momento se lanza el reflow
    Estilos de layout
    Son los estilos que cambian la disposición de elementos. P.e.: height, display, fontSize...
    Todos ellos provocan un reflow.

    Recomendaciones generales:
    • Minimizar los cambios en la propiedad style
    • Definir un CSS con todos los cambios y realizar únicamente cambios en el className
    Además del reflow, siempre hay que tener en cuenta el redraw también que es cuando se cambia la representación (no el layout) de un objeto.

    Obtención de información de layout
    "Pa'habernos matao". Resulta que si consultamos el layout de un elemento, se recalcula la disposición del documento!. Vamos, que perdemos ciclos al consultar propiedades tipo offsetWidth, scrollLeft, getProperty(“display”)...

    Recomendaciones:
    • Minimizar el acceso a la información de layout
    • Si un valor se usa más de una vez, almacenarlo en una variable local. Ésta es de perogrullo.
    Conclusiones de los dos últimos posts.
    • Cuidado con el uso de objetos de tipo HTMLCollection
    • Realizar las manipulaciones del DOM fuera del documento
    • Cambiar clases CSS en vez de estilos CSS
    • Hay que tener cuidado con cuándo se accede a la información de layout.

    2011/03/12

    Optimización de javascript (VII). Escribiendo código óptimo

    Todos (entre los que me incluyo) por desconocimiento o vagancia, hacemos código que podía ser mejor pero que por pereza u otras escusas no tan importantes queda un tanto "descuidado" usando palabras suaves.

    Buscando por "ahí" he visto varias perlas que pese a ser buenas de por sí, están desperdigadas entre mucha paja. Voy a intentar recopilarlas todas en estos posts.

    Partiendo de la premisa de que el código más óptimo es el que no se ejecuta, vemos que en algoritmos de orden 1 optimizar código puede ser un poco tonto, pero cuando subimos de orden como n o incluso n^2 y no digamos n^3 (o superiores, so bestias! qué estáis haciendo?!?!?) cada ciclo de reloj cuenta. Al turrón!

    Reducción del ámbito de ejecución
    Dentro de cada bloque se ven tanto las variables definidas dentro de ese bloque como las de todos sus padres, de modo que mientras más saltos de nivel de profundidad en el acceso a variables dentro del ámbito, más se tarda en ejecutar el código. Para reducir los saltos podemos echar mano de estas técnicas:
    • Almacenar en variables locales las variables que se encuentren fuera del ámbito actual. Especialmente con variables globales.
    • Evitar el uso de WITH ya que incrementa en uno el nivel de profundidad del ámbito. Usar variables locales en vez de WITH.
    • Cuidado con TRY-CATCH. CATCH incrementa en uno el nivel de profundidad del ámbito.
    • Usar las closures con moderación. Si no sabes qué es una closure, ahora es el momento. Si aun así te has quedado igual, piensa en que son funciones auto-ejecutadas. En jQuery se usan continuamente y son las típicas que van dentro de las llamadas: blahblahblah( function() { ...algo...} )
    • No olvidar usar VAR al definir las variables. Si no usas VAR para definir variables estás cometiendo un epic-fail porque todas tus variables serán globales!. Al próximo que se le olvide usar VAR, le corto las manos.
    Acceso a datos
    Se puede accceder a la información de 4 maneras:
    1. Literal. var nombre = “pepe”;
    2. Variable. var nombre2 = nombre;
    3. Propiedad de objeto. var nombre3 = objeto.nombre;
    4. Elemento de un array. var nombre4 = items[0];
    Conclusiones después de controlar el tiempo usado en cada tipo de acceso a la información:
    • El método más rápido es el 1. 
    • La diferencia en tiempo entre el 1 y el 2 es despreciable (si la variable es local, ojo).
    • Los métodos 3 y 4 son más costosos y depende del navegador en cuánto lo son (seguro que en IE6 es de orden n-factorial).
    • Mientras mayor sea el nivel de profundidad, más costoso. coste de objeto.nombre.name >> objeto.nombre.
    • No hay diferencia entre objeto.nombre y objeto[“name”] salvo en safari que la notación por punto es más rápida.
    Recomendaciones
    • Almacenar los casos 3 y 4 en variables locales si:
    • La propiedad de un objeto se accede más de 1 vez.
    • El elemento del array se accede más de 1 vez.
    • Minimizar el nivel de profundidad para acceder a los datos en el objto/array.
    Ejemplo:

    function procesa(datos) {
        if(datos.count > 0) {
            for(var i=0; i<datos.length; i++) {
                procesaDatos(elemento[i]);
            }
        }
    }
    Quedaría como:

    function procesa(datos) { 
        var cuenta = datos.length, 
        elemento = datos.element; 
        if(cuenta > 0) {
            for(var i=0; i<cuenta; i++) {
                procesaDatos(elemento[i]);  
            } 
        }
    }


    Sorprendentemente, la segunda función se ejecuta hasta un 33% más rápido.

    Bucles
    Aquí es donde se nos escapa el mojo, así que dependiendo de la versión de javascript con la que estemos trabajando (espero por vuestro bien que sea la última y tengáis un navegador decente... sí, Internet Explorer no está en la lista de admitidos, lo sentimos). Ya sabéis, si solo se ejecuta el código una vez (o pocas iteraciones) y no nos pasamos de los 50ms, como que no importa demasiado, pero es bueno saber en qué afectan las distintas estructuras de bucles.

    ECMA-357 javascript 2ª edición:
    • FOR-EACH --> Evitar a toda costa!. Muy bonito y legible pero mata el rendimiento.
    ECMA-262 javascript 3ª edición:
    • FOR
    • FOR-IN --> Evitar a toda costa!.
    • DO-WHILE
    • WHILE
    ECMA-262 javascript 5ª edición:
    • array.forEach --> Evitar a toda costa.
    Iteración por funciones
    • jQuery.each(), Y.each(), $each, Enumerable.each() --> Evitar a toda costa. Tardan 8x veces más.
    El uso de FOR, DO-WHILE y WHILE es indistinto a nivel de rendimiento. Solo influye la cantidad de trabajo hecha en cada iteración y el número de iteraciones por lo que para optimizar tiempos habría que reducir una u otra o las dos.

    Soluciones sencillas:
    • Almacenar la longitud en una variable local para que no se chequee cada iteración.
    • Incrementar el contador a la vez que se accede (dato = datos[i++])
    • Eliminar acceso a objetos/propiedades usando variables locales para almacenarlos.
    • Usar bucles del tipo (50% más rápidos): for(i = 0, ii = Obj.length; ii--;){}
    Hasta aquí con optimizaciones sencillas y resultonas del código.

    2011/03/11

    Optimización de javascript (VI). Conclusiones sobre interfaces responsivas

    Teniendo en cuenta las tres últimas entradas dedicadas a interfaces ágiles, se pueden llegar a las siguientes conclusiones a modo de resumen.
    • Se deben colocar los scripts al final de la página
    • Hay que concatenar los scripts en tan pocos ficheros como sea posible.
    • Elegir el método correcto de carga de scripts (carga dinámica, diferidos, asíncronos). Por supuesto todo depende de lo que queramos hacer.
    • No dejar que se ejecuten funciones javascript por más de 50ms.
    • Romper funciones excesivamente largas mediante timers o workers.
    • Cuidado con los repintados y los reflows.
    • Realizar operaciones complejas sobre el DOM fuera del documento mediante la técnica de eliminar-modificar-reinyectar u objetos de tipo DocumentFragment.
    • Agrupar juntos cambios de estilo mediante clases o modificando todo el estilo de una vez.
    • Evitar reflows accidentales al realizar un código descuidado.

    2011/03/10

    Optimización de javascript (V). Repintado y reflow

    Las tareas de repintado y reflow pueden hacer también que la UI no sea suficientemente ágil ya que pueden tardar mucho en realizarse si no se hacen inteligentemente.

    Los cambios en la apariencia de elementos, como por ejemplo el color, fuerzan repintados y los cambios en la estructura (layout) fuerzan el reflow de los elementos de la página.
    Siempre se realizan reflows cuando:
    • Se hace la carga inicial de la página
    • Se redimensiona la ventana del navegador
    • Se añaden o eliminan nodos del DOM
    • Se aplican estilos de layout
    • Se obtiene información de layout
    Los repintados y reflows se encolan mientras se ejecuta el javascript para pasar a ser efectivos una vez se termina la ejecución, realizándose los cambios uno tras otro en orden. De esta manera si en un bucle se hacer 10 operaciones que fuercen un reflow, ese reflow se ejecutará 10 veces una vez termine de ejecutarse el código!.

    Para evitar demasiadas ejecuciones de repintado y reflow, se pueden usar las siguientes técnicas.
    Realizar manipulaciones del DOM fuera del documento

    La ventaja es que mientras se manupulan los objetos fuera del documento, no se fuerzan ni repintados ni reflows. Las principales técnicas son:
    • Quitar elemento del documento, hacer las manipulaciones en el elemento, reinyectarlo en el documento.
    • Poner su propiedad de display a none, hacer los cambios, poner display de nuevo al valor original.
    • Realizar los cambios en un DocumentFragment y luego aplicarlas todas de golpe al añadir el fragmento al realizar el appendChild().
    Técnica de DocumentFragment
    Toda la composición se realiza fuera del documento por lo que no hay cambios hasta que se aplica.
    // Ejemplo de DocumentFragment
    var list = document.GetElementByClassName("items")[0],
       fragment = document.createDocumentFragment(),
       i, item;
    for(i=0; i < 10; i++) {
            item = document.createElement("li");
            item.innerHTML = “Elemento #” + i;
            fragment.appendChild(item);
    }
    list.appendChild(fragment); // En este momento se lanza el reflow
    Técnica de agrupación de los cambios de estilo
    Si se cambian las propiedades de estilo de una en una, cada cambio puede dar lugar a un repintado o un reflow. Para evitarlo, se puede prediseñar la apriencia final en una clase CSS y luego en el código cambiar el className de modo que todos los cambios se hacen una única vez.
    elemento.className = "estiloFinal";
    También se puede hacer accediendo a la propiedad cssText
    elemento.style.cssText = "color:red;height:100px;"; // un único reflow
    Técnica de evitación de reflows accidentales
    Cuando se consultan propiedades de layout, se fuerzan reflows (p.e. offsetWidth). Por ello se recomienda:
    • Minimizar el acceso a la información de layout
      • offsetTop, offsetLeft, offsetWidth, offsetHeight
      • scrollTop, scrollLeft, scrollWidth, scrollHeight
      • clientTop, clientLeft, clientWidth, clientHeight
      • La mayoría de los estilos calculados
    • Si un valor se usa más de una vez, almacenarlo en una variable local

      2011/03/09

      La importancia de tirar de la cadena

      Para evitar que la carga de la página web bloquee la carga de otros componentes, una cosa importante que hay que hacer siempre en PHP es empezar pronto, y eso se hace con la instrucción flush(), de modo que correctamente colocada, nos dará una ventaja muy grande en la carga de la página.

      El sitio correcto es entre el <head> y el <body> ya que ese momento supuestamente ya tendríamos definido dónde están los css y los js.
      <html>
          <head>
              ... scripts y css ...
          </head>
          <?php flush(); ?>
          <body>
              ...

      Trigonometría aplicada

      Hace unos días me he dado cuenta de lo oxidado que tengo el tema de la trigonometría y más cuando desde siempre he estado muy interesado en la computación gráfica, raytracers, técnicas de representación gráfica, etc... incluso me gustaba la asignatura de álgebra en la facultad y la hasta la usaba!. Es el momento de tirar de wikipedia y refrescar los conocimientos...

      En fin, no está de más un repaso rápido al A-B-C de trigonometría y su aplicación inmediata, ya que el 90% de los casos se resuelven con el 1% del conocimiento.

      Lo que voy a poner es el resultado, los que estén interesados en el desarrollo de la fórmula, que le pregunten a un matemático.

      Partimos de un objeto coordenada
      // **** Coord Object ****
      function Coord(x, y) {
          this.x = x;
          this.y = y;
      }
      Empezando por lo más básico, seno y coseno...

      * Pitágoras y los tiángulos. A partir de un origen o, una distancia d y un ángulo a respecto a la horizontal necesito saber la coordenada (x,y) para colocar algo allí.
      var c = new Coord(o.x + d * Math.cos(a), o.y + d * Math.sin(a));
      * Ángulo entre 2 objetos. Teniendo 2 coordenadas m y o, necesito saber el ángulo que hacen entre sí siendo m la coordenada del ratón por ejemplo y o la coordenada del centro del objeto con el que quiero comparar m.
      var a = Math.atan2(m.y - o.y, m.x - o.x);
      * Distancia entre 2 puntos c1 y c2. Ni que decir tiene, que ambos deben estar en las mismas unidades.
      var d = Math.sqrt(Math.pow((c2.x - c1.x), 2) + Math.pow((c2.y - c1.y), 2));
      * Traslaciones. Necesito mover un punto c1 en una cantidad indicada por el vector o siendo el resultado c2. Ya se que es una chorradita, pero esta es la manera general de realizar transformaciones afines. La matriz de la traslación sería.

      | 1   0  o.x | | c1.x |   | c1.x + o.x |
      | 0   1  o.y | | c1.y | = | c1.y + o.y |
      | 0   0   1  | |   1  |   |      1     |

      Con todo ésto, para trasladar, solo habría que hacer
      var c2 = new Coord(c1.x + o.x, c1.y + o.y);
      * Transformaciones. Escalados, deformaciones, proyecciones, transformaciones en espejo, etc...  Aquí es donde tiene sentido la "chorradita" de la traslación, ya que ésta es un caso particular de transformación. Todas ellas se pueden combinar simplemente poniendo el valor correcto en la posición de la matriz de antes. Para más detalles, la wikipedia.

      * Rotaciones. Otro caso especial de transformación. Para realizarlas, necesitamos definir una matriz de rotación (suponemos 2 dimensiones aunque para 3, es muy parecido). Si se quiere rotar un punto c1 un ángulo a alrededor de otro punto origen o (estamos dando una traslación como implícita, ojo), la matriz sería la siguiente, dando como resultado c2. Ojo, hay que pre-trasladar el punto de interés c1 al origen de la referencia (punto o) antes de empezar ya que o será nuestro (0, 0) para la rotación. Luego se hace la traslación contraria.
      | Math.cos(a)  -Math.sin(a) | | c1.x - o.x | = | c2.x |
      | Math.sin(a)   Math.cos(a) | | c1.y - o.y |   | c2.y |
      Por lo tanto, expresándolo todo junto programáticamete
      var c2 = new Coord(0, 0); // Voy a separarlo en 2 líneas por claridad
      c2.x = o.x + (Math.cos(a) * (c1.x - o.x)) - (Math.sin(a) * (c1.y - o.y));
      c2.y = o.y + (Math.sin(a) * (c1.x - o.x)) + (Math.cos(a) * (c1.y - o.y));
      Para 3 dimensiones, os lo dejo como deberes, pero es más de lo mismo.

      * Trazado de líneas (a.k.a. trasladar un objeto desde el punto a al punto b). Solo una palabra... Bresenham. Es un algoritmo óptimo únicamente con aritmética entera. Pseudocódigo del algoritmo plagiado vilmente desde la wikipedia:
      function line(x0, x1, y0, y1)
           boolean steep := abs(y1 - y0) > abs(x1 - x0)
           if steep then swap(x0, y0), swap(x1, y1)
           if x0 > x1 then swap(x0, x1), swap(y0, y1)
           int deltax := abs(x1 - x0)
           int deltay := abs(y1 - y0)
           int error := deltax / 2
           int ystep
           int y := y0

           int inc
           if x0 < x1 then inc := 1 else inc := -1
           if y0 < y1 then ystep := 1 else ystep := -1
           for x from x0 to x1 with increment inc
               if steep then plot(y,x) else plot(x,y)
               error := error - deltay
               if error < 0 then
                   y := y + ystep
                   error := error + deltax
      Para los vagos (como yo), ya escrito en javascript:
      function calcStraightLine (c1, c2) {
          var coords = new Array();
          var x1 = c1.x; var y1 = c1.y; var x2 = c2.x; var y2 = c2.y;
          // Calcula las diferencias t el chequeo de error
          var dx = Math.abs(x2 - x1);
          var dy = Math.abs(y2 - y1);
          var sx = (x1 < x2) ? 1 : -1;
          var sy = (y1 < y2) ? 1 : -1;
          var err = dx - dy;
          // Fija la coordenara origen
          coords.push(new Coordinates(y1, x1));
          // Bucle principal
          while (!((x1 == x2) && (y1 == y2))) {
              var e2 = err << 1;
              if (e2 > -dy) {
                  err -= dy;
                  x1 += sx;
              }
              if (e2 < dx) {
                  err += dx;
                  y1 += sy;
              }
              // Guarda la coordenada calculada
              coords.push(new Coordinates(y1, x1));
          }
          return coords;
      }
      * Trazado de circunferencias. Para dibujarlo se hace por simetría en los 8 octantes usando el algoritmo del punto medio.

      Funcion PuntoMedio (int xc, yc, float R)
          Pintar Pixel (0, R)
          Calcular p0 = 5/4 – R // si R es entero, p0 = 1-r
          Para cada xk
              si pk < 0
                  Pintar Pixel (xk+1, yk)
                  pk+1 = pk + 2xk + 3
              si pk > 0
                  Pintar Pixel (xk+1, yk-1)
                  pk+1 = pk + 2xk – 2yk+ 5
          Determinar por simetría los puntos de los otros 7 octantes
          Pintar Pixel (x+xc, y+yc)
      Hasta aquí hemos llegado.