transform-origin: el punto sobre el que gira todo
Cómo se resuelve el origen de transformación, por qué es el 50% 50% de una caja que quizá no es la que crees, y qué caja elige transform-box en HTML y en SVG.
Una rotación no es una rotación: es una rotación alrededor de un punto. transform-origin define ese punto, y con él el resultado de cualquier giro, escalado o sesgo. Es la propiedad que más silenciosamente arruina un efecto —el elemento gira, pero no por donde querías— y también la que resuelve de una línea problemas que parecen exigir matemáticas.
- Escribir
transform-origincon las tres formas de valor que acepta. - Predecir el resultado de un giro conociendo el origen y viceversa.
- Explicar qué caja de referencia usa el origen en HTML y elegir otra con
transform-box. - Usar el tercer valor del origen para colocar el eje de giro en profundidad.
Qué es y cómo se escribe
transform-origin es el punto fijo de la transformación: el único punto de la caja que no se mueve cuando rotas o escalas. Su valor inicial es 50% 50%, el centro. Acepta uno, dos o tres valores.
.a { transform-origin: center; } /* equivale a 50% 50% */
.b { transform-origin: top left; } /* equivale a 0% 0% */
.c { transform-origin: 0 100%; } /* esquina inferior izquierda */
.d { transform-origin: 20px 30px; } /* longitudes absolutas desde arriba a la izquierda */
.e { transform-origin: 50% 50% -100px; }/* el tercer valor es la z */
Las palabras clave siguen la convención de background-position: left, center, right para el eje horizontal y top, center, bottom para el vertical. Con dos valores en palabras clave el orden es libre —top left y left top son lo mismo— pero en cuanto mezclas una longitud o un porcentaje, el primer valor es el horizontal y el segundo el vertical.
Los porcentajes se resuelven contra el tamaño de la caja de referencia del propio elemento. Un transform-origin: 100% 0 en un elemento de 200 por 80 píxeles equivale a 200px 0.
El tercer valor, la coordenada z, solo admite longitudes: no hay una dimensión de profundidad contra la que resolver un porcentaje. Sirve para mover el eje de giro hacia adelante o hacia atrás del plano del elemento, y es lo que necesitas cuando construyes un cubo o un carrusel 3D, donde cada cara gira alrededor de un eje que está en el centro del cuerpo, no en el centro de la cara.
El caso que siempre se olvida
El error más común es escalar desde el centro cuando el diseño pide crecer desde un borde. Una barra de progreso, un menú que se despliega, un tooltip que aparece: todos crecen desde un anclaje, no desde su punto medio.
/* barra de progreso que crece desde la izquierda */
.barra { inline-size: 100%; transform-origin: left center; scale: var(--p) 1; }
/* menú que se despliega desde su borde superior */
.menu { transform-origin: top center; scale: 1 0; transition: scale .2s; }
.menu[data-abierto] { scale: 1 1; }
Escalar con scale en lugar de animar inline-size o block-size tiene una ventaja de rendimiento real: el escalado se resuelve en la fase de composición y no obliga a rehacer el layout. El coste es que el contenido se deforma con la caja —el texto de una barra que se estira se estira también—, así que el patrón sirve para superficies planas y no para cajas con texto dentro.
El segundo caso frecuente es el pivote fuera de la caja. Un porcentaje mayor que 100 o negativo es perfectamente válido, y coloca el origen fuera del elemento. Una puerta que gira sobre una bisagra situada 20 píxeles a su izquierda es transform-origin: -20px 50%.
transform-box: qué caja se mide
Los porcentajes de transform-origin necesitan una caja de referencia. En HTML esa caja es, por defecto, la caja de borde del elemento. Eso significa que el padding y el borde cuentan: en un elemento con padding: 20px, el 50% 50% está en el centro incluyendo el padding, no en el centro del contenido.
transform-box te deja elegir otra. Sus valores relevantes son content-box, padding-box, border-box, fill-box, stroke-box y view-box. En HTML el valor inicial es view-box, que para un elemento HTML se comporta como border-box.
Donde la propiedad se gana el sueldo es en SVG. Históricamente, un elemento SVG transformado con CSS resolvía su origen respecto al sistema de coordenadas del viewBox del SVG entero, no respecto a la forma. La consecuencia es memorable: pones transform-origin: center a un círculo que está en la esquina y el círculo gira alrededor del centro del lienzo, describiendo una órbita enorme.
/* girar un path sobre su propio centro dentro de un SVG */
svg .aguja {
transform-box: fill-box;
transform-origin: center;
rotate: 45deg;
}
fill-box usa la caja delimitadora del objeto —lo que en SVG se llama el bounding box— y con ella center significa por fin el centro de la forma. stroke-box incluye además el grosor del trazo, útil cuando el trazo es grueso y su asimetría desplaza el centro visual.
flowchart TB
A[Necesitas un origen de transformacion] --> B{El elemento es HTML o SVG}
B -->|HTML| C[La caja por defecto ya es la de borde]
C --> D{Te molesta que el padding cuente}
D -->|No| E[No toques transform-box]
D -->|Si| F[transform-box content-box]
B -->|SVG| G[Por defecto se mide el viewBox entero]
G --> H[El giro parece una orbita]
H --> I[transform-box fill-box]
I --> J[Ahora center es el centro de la forma]
style A fill:#89b4fa,color:#11111b
style E fill:#a6e3a1,color:#11111b
style F fill:#a6e3a1,color:#11111b
style H fill:#f38ba8,color:#11111b
style J fill:#a6e3a1,color:#11111btransform-origin es una propiedad animable, y ahí hay una trampa que solo se descubre depurando. El origen se resuelve después del layout del elemento, contra su caja ya dimensionada, y se aplica antes de la matriz de transformación. Si animas el origen y la transformación a la vez, lo que ves no es una interpolación entre dos poses: es una interpolación entre dos sistemas de referencia distintos, y el resultado tiene una deriva que no está en ninguno de los dos extremos. Un elemento que gira 90 grados mientras su origen pasa de left a right no describe ningún arco reconocible; describe una curva que depende de la velocidad relativa de las dos interpolaciones. La regla operativa es sencilla y ahorra horas: el origen se declara, no se anima. Si de verdad necesitas cambiar el pivote a mitad de un movimiento, la forma correcta es anidar dos elementos, cada uno con su origen fijo, y animar cada uno por separado. Es exactamente lo que hace cualquier sistema de animación esquelética: los pivotes son estructura, no señal.
- Haz que un rectángulo gire alrededor de su esquina inferior derecha y comprueba que esa esquina no se mueve ni un píxel.
- Construye una barra de progreso con
scaleytransform-origin: lefty comprueba en el panel de rendimiento que no dispara layout. - Coloca el origen en
-40px 50%y observa la órbita que describe el elemento al rotar 360 grados. - Mete un
pathen un SVG lejos del origen delviewBox, rótalo contransform-origin: centery arregla el resultado contransform-box: fill-box. - Construye una cara de cubo con
transform-origin: 50% 50% -100pxy comprueba a qué distancia queda del centro de giro.