wandres.dev
SVG: EL DOCUMENTO · Un lenguaje, no un formato

preserveAspectRatio con su sintaxis completa

Controlar qué pasa cuando las proporciones del viewBox no coinciden con las del hueco: las diez alineaciones, meet frente a slice, y dónde más se aplica el atributo.

⏱ 16 min

En cuanto las proporciones del viewBox y las del hueco no coinciden, hay que decidir tres cosas: si el dibujo se deforma, si sobra espacio o si se sale, y hacia qué lado se apoya lo que quede. preserveAspectRatio responde a las tres con un solo valor de dos palabras, y su sintaxis es de las pocas de la plataforma que distingue mayúsculas dentro del propio valor, lo que la convierte en una fuente inagotable de atributos que no hacen nada.

🎯 Al terminar esta lección sabrás
  • Escribir el valor completo con su alineación y su modo de ajuste.
  • Distinguir meet, slice y none y su equivalencia con object-fit.
  • Aplicarlo también en elementos anidados y en imágenes.
  • Diagnosticar por qué un dibujo aparece descentrado cuando el atributo es correcto.

La sintaxis

preserveAspectRatio="<alineacion> [<meetOrSlice>]"

La alineación dice hacia dónde se apoya el contenido cuando sobra o falta espacio. Se compone del eje X y del eje Y pegados, cada uno con tres posibilidades:

Valor X Y
xMinYMin Izquierda Arriba
xMidYMin Centro Arriba
xMaxYMin Derecha Arriba
xMinYMid Izquierda Centro
xMidYMid Centro Centro
xMaxYMid Derecha Centro
xMinYMax Izquierda Abajo
xMidYMax Centro Abajo
xMaxYMax Derecha Abajo

Más el valor especial none, que desactiva la conservación de proporciones.

La capitalización es exactamente esa: x minúscula, Min, Mid o Max con la primera mayúscula, Y mayúscula. xmidymid no es un valor válido y el navegador lo descarta volviendo al valor por defecto, sin decir nada. Es el error número uno con este atributo.

El modo de ajuste admite dos palabras y el valor por defecto de todo el atributo es xMidYMid meet.

meet, slice y none

meet escala hasta que el dibujo cabe entero. La escala es la menor de las dos proporciones, sobra espacio en uno de los ejes y ese sobrante se reparte según la alineación. Equivale a object-fit: contain.

slice escala hasta que el dibujo cubre todo el hueco. La escala es la mayor de las dos proporciones, sobra dibujo en uno de los ejes y ese exceso queda fuera del viewport, recortado. Equivale a object-fit: cover, y aquí la alineación decide qué parte del dibujo sobrevive al recorte.

none escala cada eje por su cuenta hasta llenar el hueco exactamente. El dibujo se deforma. Equivale a object-fit: fill. Cuando se usa none, la alineación no pinta nada porque no sobra ni falta espacio.

<!-- Cabe entero, centrado: el valor por defecto -->
<svg viewBox="0 0 100 100" width="300" height="150"
     preserveAspectRatio="xMidYMid meet"></svg>

<!-- Cubre el hueco recortando por los lados, conservando la parte superior -->
<svg viewBox="0 0 100 100" width="300" height="150"
     preserveAspectRatio="xMidYMin slice"></svg>

<!-- Se estira hasta llenar, deformando -->
<svg viewBox="0 0 100 100" width="300" height="150"
     preserveAspectRatio="none"></svg>

none tiene un uso legítimo y uno pésimo. El legítimo son las gráficas que deben ocupar exactamente su caja y cuyo contenido no sufre por la deformación: una línea de tendencia, una barra de progreso, un fondo con ondas. En esos casos, none combinado con vector-effect: non-scaling-stroke da un resultado impecable, porque la línea se estira sin engordar.

<svg viewBox="0 0 100 20" preserveAspectRatio="none"
     style="width:100%;height:40px;display:block">
  <polyline points="0,16 12,9 24,13 36,4 48,10 60,2 72,7 84,3 100,6"
            fill="none" stroke="#a6e3a1" stroke-width="2"
            vector-effect="non-scaling-stroke"/>
</svg>

Sin non-scaling-stroke, esa misma línea aparecería con un grosor distinto en cada tramo según la deformación de cada eje, que es el aspecto inconfundible de un SVG mal estirado.

El uso pésimo es aplicar none a un logotipo o a un icono. La deformación de un dibujo con círculos o texto se ve inmediatamente y da sensación de descuido.

Dónde más se aplica

El atributo no es exclusivo del svg raíz. Aparece en varios sitios y en todos significa lo mismo: cómo encajar un rectángulo de contenido dentro de otro.

En un svg anidado. Un svg dentro de otro establece un viewport nuevo con su propio viewBox, y por tanto su propio ajuste. Es la manera de tener una zona del dibujo con reglas de escalado distintas de las del resto.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100" width="400" height="200">
  <rect width="200" height="100" fill="#313244"/>
  <svg x="10" y="10" width="60" height="80" viewBox="0 0 24 24"
       preserveAspectRatio="xMidYMid meet">
    <circle cx="12" cy="12" r="10" fill="#89b4fa"/>
  </svg>
</svg>

En image. El elemento image de SVG coloca un mapa de bits, y preserveAspectRatio decide cómo encaja en el rectángulo que le has dado. Es el object-fit del mundo SVG.

En pattern, marker y view. Todos tienen contenido con su propio sistema de coordenadas que hay que encajar en un hueco.

Queda una palabra clave heredada que conviene reconocer al verla: defer, que podía preceder a la alineación en image para indicar que se respetara el ajuste del propio fichero referenciado. Está obsoleta en SVG 2 y los navegadores la ignoran; si aparece en un SVG antiguo, se puede borrar.

Cuando el dibujo sale descentrado y el atributo es correcto, el problema está en el viewBox, no aquí

El síntoma es este: pones xMidYMid, que centra, y el dibujo aparece desplazado hacia arriba y a la izquierda. Revisas el atributo diez veces, está bien escrito, y sigue descentrado. La causa casi nunca es preserveAspectRatio, porque este atributo hace exactamente una cosa: centra el rectángulo del viewBox dentro del hueco. Si el dibujo no está centrado dentro de su propio viewBox, se centrará el rectángulo y el dibujo quedará donde estaba dentro de él, es decir, descentrado. Esto ocurre constantemente con los SVG exportados desde herramientas de diseño, que suelen escribir un viewBox que corresponde a la mesa de trabajo y no al contenido: un icono dibujado en la esquina de un lienzo de 512 mantiene su esquina aunque tú centres el lienzo. La comprobación es de dos líneas en la consola: svg.getBBox() te da la caja real del contenido, y si su origen no es cero o si su tamaño no coincide con los dos últimos números del viewBox, ya sabes dónde está el desajuste. La corrección definitiva es reescribir el viewBox a partir de esa caja, con el margen que quieras, tal y como se hace en la lección del viewBox. Hay dos variantes del mismo malentendido que conviene identificar de una vez. La primera: el “aire” invisible. Un grupo con un rectángulo transparente de fondo —que muchas herramientas añaden para fijar el tamaño de exportación— forma parte de la caja del contenido aunque no se vea, así que tu icono parece descentrado porque está centrado junto con un rectángulo que nadie ve. Borrar ese rectángulo cambia el encuadre de golpe. La segunda: alinear con preserveAspectRatio lo que se debería alinear con el viewBox. Se ve mucho: alguien pone xMinYMin para “pegar el dibujo a la izquierda” cuando lo que quiere es que el dibujo llene el hueco, y acaba con un icono pequeño en una esquina. Las dos herramientas resuelven cosas distintas y la regla es limpia: el viewBox decide qué parte del dibujo se ve; preserveAspectRatio decide qué hacer con el espacio que sobra después. Si tienes espacio que sobra y no lo querías, el que está mal es el viewBox.

Casos reales

Cuatro situaciones que cubren casi todo lo que se necesita en la práctica.

Un icono en un botón. Valor por defecto, sin tocar nada. El viewBox ajustado al dibujo y el tamaño desde el CSS.

Una cabecera decorativa que debe llenar el ancho sin bandas. slice con la alineación puesta en la parte que interesa conservar:

<svg viewBox="0 0 1200 400" preserveAspectRatio="xMidYMin slice"
     style="width:100%;height:220px;display:block">
  <!-- la parte de arriba del dibujo nunca se recorta -->
</svg>

Una gráfica que debe encajar exactamente en su caja. none más non-scaling-stroke en todo lo que sea línea.

Un SVG que debe comportarse como una imagen normal. Valor por defecto y tamaño con CSS; y si lo cargas con la etiqueta de imagen, recuerda que puedes combinar el preserveAspectRatio interno con el object-fit del elemento, y que los dos se aplican: primero el SVG encaja su dibujo en su propio viewport, y después el navegador encaja esa imagen resultante en la caja del elemento. Cuando algo se ve doblemente recortado, casi siempre es porque hay un slice dentro y un cover fuera.