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.
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.
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.