Rotar elementos con CSS, plugin jQuery multinavegador

31 Mar 2011

Resultado Final jQuery Cross-Browser Rotate Plugin

jQuery Crossbrowser Rotate Plugin

Introducción

Una de las novedades más interesantes de CSS3 es la posibilidad de aplicar transformaciones directamente a los elementos de una página. Con ellas, podemos rotar, estirar y desplazar prácticamente cualquier cosa sin necesidad de utilizar Javascript, canvas o Flash.

De entre las anteriores, encuentro la rotación como la más útil para temas de maquetación y desarrollo de aplicaciones gráficas pero, dada la cantidad de dispositivos y navegadores diferentes, no siempre resulta sencilla de aplicar.

En teoría, basta con declarar una regla de estilo indicando los grados con que queremos girar nuestro elemento:

.rotate{
  transform: rotate(90deg);
}

Como suele ser habitual, cada navegador tiene sus particularidades aunque mantienen en general un estándar:

.rotate{
  transform: rotate(90deg);
  -moz-transform: rotate(90deg); // Mozilla
  -o-transform: rotate(90deg); // Opera
  -webkit-transform: rotate(90deg); // Webkit
}

Internet Explorer. Un mundo diferente

Pero como siempre, al llegar a IE, el tema varía considerablemente: para rotar un elemento tenemos que recurrir a los filtros de transformación del navegador:

filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);

La propiedad acepta cuatro valores: 0, 1, 2 o 3, los cuales rotan un elemento 0, 90, 180 o 270 grados respectivamente.

Para conseguir un grado de rotación diferente, hay que aplicar un objeto Matrix y es ahí donde la cosa se complica de un modo absurdo.

IE Matrix

Una transformación Matrix necesita ser definida en el elemento antes de comenzar a operar con ella:

filter:progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand')

Una vez iniciado, el filtro requiere de conocimiento básicos de álgebra lineal con matrices y vectores. Wikipedia tiene una entrada muy completa al respecto. Un vistazo a ese artículo puede darnos una idea de la complejidad del asunto.

Esto se puede portar a Javascript con la siguiente estructura:

var angle = 45,
    rad = angle * Math.PI * 2 / 360,
    cos = Math.cos(rad),
    sin = Math.sin(rad);
 
var obj = document.GetElementById('objToRotate');
obj.style.filter = "progid:DXImageTransform.Microsoft.Matrix();";
obj.filters.item("DXImageTransform.Microsoft.Matrix").SizingMethod = "auto expand";
obj.filters.item("DXImageTransform.Microsoft.Matrix").FilterType = "bilinear";
obj.filters.item("DXImageTransform.Microsoft.Matrix").M11 = cos;
obj.filters.item("DXImageTransform.Microsoft.Matrix").M12 = (-sin);
obj.filters.item("DXImageTransform.Microsoft.Matrix").M21 = sin;
obj.filters.item("DXImageTransform.Microsoft.Matrix").M22 = cos;

La idea es realizar una conversión de grados a radianes y luego utilizar las funciones de seno y coseno para calcular los cuatro valores de la matriz (M11, M12, M21 y M22).

Por ejemplo, para aplicar 45 grados a un objeto, tendríamos algo parecido a:

filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);

A simple vista el código inline resulta intimidatorio. Con un gran esfuerzo de ingeniería inversa, se podría calcular todos los valores de una matriz para cualquier ángulo posible, pero no parece que sea una solución elegante.

Por si no fuera poco, cuando IE rota un elemento utilizando el filtro, no lo hace manteniedo su centro tal y como ocurre con CSS. Esto exige de un nuevo esfuerzo para recalcular la posición del pivote y reubicarla ya con los métodos tradicionales de top y left.

Transformaciones CSS CrossBrowser

NOTA: La imagen anterior ha sido extraída del artículo Cross Browser CSS Transforms.

Simplificando el trabajo en un plugin jQuery

Para simplificar todo lo anterior, he creado un plugin jQuery que automáticamente comprueba el navegador del usuario y aplica, o bien las reglas CSS3 si tienen soporte, o bien realiza los cálculos necesarios para activar y aplicar el filtro en IE.

La sintaxis del nuevo método, rotate, es muy sencilla:

.rotate( options(Object) )

Donde:
options es un objeto que acepta las siguientes opciones:
angle (Int): son los grados que se aplicarán a la rotación.
center (Boolean) (Opcional): fija el centro del objeto para las transformaciones en IE. Su valor por defecto es verdadero.

Para rotar, por ejemplo 45 grados un objeto, el código sería:

$('#myObj').rotate({
  angle : 45
});

El plugin es compatible con los principales navegadores (IE5.5+, Firefox, Opera, Chrome y Safari) y ocupa tan solo 1,5kb en su versión minificada.

NOTA: La demo utiliza algunos recursos de maquetación no disponibles en versiones anteriores del IE7, por lo que puede no verse o funcionar correctamente.

Más:

{9} Comentarios.

  1. más que HTML

    En el caso de que se quiera o pueda usar Javascript, en css3please.com hay un generador (que entre otras muchas propiedades) te genera el código CSS de rotación, incluyendo Opera e Internet explorer.

    • Carlos Benítez

      Efectivamente, Paul Irish y compañía ha hecho un gran trabajo con el proyecto css3please.com. Sin embargo, el código que generan es estático; es por esto que hacía falta una solución vía Javascript para conseguir el mismo efecto de una forma dinámica como se muestra en la demo.

      Gracias por recordarnos este estupendo recurso.
      Saludos!

  2. Pablo Mendoza

    Para corregir el origen de rotación del filtro se puede ver en esta página: https://github.com/heygrady/transform/wiki/correcting-transform-origin-and-translate-in-ie

  3. Gracias, un recurso que no es habitual y muy util.

  4. Yuri

    Excelente Plugin. Saludes desde Colombia-Choco.

  5. paco

    Y despues que se rota la imagen, como se puede guardar la nueva imagen rotada?

    Gracias

    • Carlos Benítez

      Hola;
      cuando giramos la imagen no podemos guardarla ya que estamos actuando directamente sobre su contenedor en el DOM: es solo un efecto que no modifica a la propia imagen. Si quisiéramos guardar una nueva, tendríamos que utilizar canvas para girarla y, desde ahí, con los métodos del API, crear una nueva imagen.

      Desgraciadamente, no era ese el propósito del artículo.

      En este magnífico post, podemos ver cómo se rotaría una imagen en canvas:
      http://creativejs.com/2012/01/day-10-drawing-rotated-images-into-canvas/

      Y, para guardar el resultado en una imagen:
      http://www.nihilogic.dk/labs/canvas2image/

      Tendrías que mezclar los dos pasos anteriores para lograr girar una imagen y guardar su resultado en una nueva.

      Saludos!

  6. paco

    Muchas gracias Carlos

    Ya me pongo a trabajar en estos ejemplos y si quedan bien te paso el enlace para que me los califiques.

  7. Miguel

    excelente.!!!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *