El círculo SVG debe adaptarse a la altura de la cuadrícula CSS sin superponerse

8

En el siguiente ejemplo, el círculo se adapta al ancho de la cuadrícula y mantiene su relación de aspecto. A medida que el ancho del contenedor se reduce, los círculos se reducen con él ...

Sin embargo, cuando la altura es menor que el ancho (segundo recuadro), el círculo no se contrae solapando fuera de la cuadrícula. ¿Hay alguna manera de que también se adapte a la altura manteniendo la relación de aspecto?

.container {
	display: grid;
	background-color: greenyellow;
	margin: 5px;
	min-height: 10px;
	min-width: 10px;
}

.portrait {
		max-height: 100px;
		max-width: 200px;
}

.landscape {
		max-height: 200px;
		max-width: 100px;
}

.aspect-ratio {
	grid-column: 1;
	grid-row: 1;
	background-color: deeppink;
	border-radius: 50%;
 align-self: center;
	justify-self: center;
}
<div class="container landscape">
  <svg class="aspect-ratio" viewBox="0 0 1 1"></svg>
</div>

<div class="container portrait">
  <svg class="aspect-ratio" viewBox="0 0 1 1"></svg>
</div>

El resultado debería verse así: ingrese la descripción de la imagen aquí

Th3S4mur41
fuente
¿Tiene la posibilidad de editar el código svg?
Thetont

Respuestas:

8

Después de jugar un tiempo con este problema, pensé que no era un problema con el diseño de la cuadrícula.

Como nunca define altura / altura máxima o ancho / ancho máximo para su SVG, lo que se calcula es width:autoyheight:auto

Nota: menciono max-height / max-width ya que estos tendrían prioridad sobre los valores de ancho / alto .


Ahora bien, ¿por qué funciona su diseño de paisaje y su diseño de retrato no funciona? Porque el ancho "tiene prioridad" sobre su altura.

Y es por eso que, en el diseño de bloque, una altura automática se basa en la altura total de los descendientes y un ancho automático se basa en el ancho del bloque que lo contiene.

Fuente: ¿por qué ancho: auto se comporta de manera diferente que altura: auto?

Esto significa que su SVG está tomando el widthde su contenedor y el heightde sus descendientes.

En su ejemplo, su SVG no tiene descendientes, pero tiene un padre con un definido ancho máximo . Pero, ¿qué significa esto para la altura del SVG ?

El SVG está calculando su altura a través de la relación intrínseca:

Si el 'viewBox' en el elemento 'svg' está correctamente especificado:

  1. deje que viewbox sea el viewbox definido por el atributo 'viewBox' en el elemento 'svg'
  2. return viewbox.width / viewbox.height

Fuente

Y tu relación intrínseca es 1/1 = 1

Entonces estás forzando tu height=width


Las soluciones)

La solución es establecer una widtho unamargin a su SVG en función de la proporción de su padre div.

En el ejemplo que dio, tiene una proporción de 2: 1, por lo que su SVG debería tener width:50%omargin: 0 25% .

Esto significa que si desea establecer una proporción diferente, debe ajustar en consecuencia (vea el retrato 1-3 en el código a continuación).

Se podría evitar establecer una regla CSS para cada proporción si heightno es una restricción, ya que podría definir un widthpara su SVG y contenedor, permitiendo que los elementos ajusten elheight para mantener su relación de aspecto.

También he agregado el último ejemplo si a alguien no le importa mantener la relación de aspecto, pero necesita contener el SVG en un conjunto heighty widthcontenedor.

.container {
  display: grid;
  background-color: greenyellow;
  margin: 5px;
  min-height: 10px;
  min-width: 10px;
}

.portrait {
  max-height: 100px;
  max-width: 200px;
}


/* Add 25% margin left and right to center the image  OR set the image width to 50% */

.portrait>.aspect-ratio {
  margin: 0 25%;
  /*
    or 
    
    width:50%;
  */
}

.landscape {
  max-height: 200px;
  max-width: 100px;
}

.aspect-ratio {
  grid-column: 1;
  grid-row: 1;
  background-color: deeppink;
  border-radius: 50%;
  align-self: center;
  justify-self: center;
}


/* Set fixed max-height and max-width rule (33% proportion)  */

.portrait-1-3 {
  max-height: 100px;
  max-width: 300px;
}


/* Align image with the new proportion */

.portrait-1-3>.aspect-ratio {
  margin: 0 33.33%;
  /*
    or 
    
    width:33.3%;
  */
}


/* Removed max-height and let the container adjust the height according to the max-width rule and the proportion set  */

.portrait-any-height {
  max-width: 400px;
}


/* Height will be adjusted through the width/margin defined here, so 10% width will correspond to 40px of height (10%*400px)*(aspect ratio=1)*/

.portrait-any-height>.aspect-ratio {
   width:10%;
}


/* Set fixed max-height and max-width rule (proportion doesn't matter)  */

.portrait-squeezed {
  max-height: 100px;
  max-width: 300px;
}


/* Make sure SVG complies with the the given max with and height (squeezing your svg)  */

.portrait-squeezed>.aspect-ratio {
  max-height: inherit;
  max-width: inherit;
}
<div class="container landscape">
  <svg class="aspect-ratio" viewBox="0 0 1 1"></svg>
</div>

<div class="container portrait">
  <svg class="aspect-ratio" viewBox="0 0 1 1"></svg>
</div>

<div class="container portrait-1-3">
  <svg class="aspect-ratio" viewBox="0 0 1 1"></svg>
</div>

<div class="container portrait-any-height">
  <svg class="aspect-ratio" viewBox="0 0 1 1"></svg>
</div>

<div class="container portrait-squeezed">
  <svg class="aspect-ratio" viewBox="0 0 1 1"></svg>
</div>

Tenga en cuenta que no soy muy competente trabajando con SVG y esta respuesta es el resultado de la investigación y mucha experimentación. Estaré encantado de recibir cualquier ajuste y / o corrección a mi respuesta.

H. Figueiredo
fuente
Gracias por la explicación detallada ... Debo notar que su investigación desafortunadamente arrojó los mismos resultados que la mía. Parece que no hay solución para mis requisitos: 1. La relación de aspecto SVG es fija. 2. la relación de aspecto del contenedor es variable / desconocida 3. el tamaño del contenedor está limitado por la altura y el ancho dependiendo del entorno (por ejemplo, el tamaño de la ventana del navegador) La solución propuesta al establecer el ancho SVG en un cierto porcentaje del contenedor, funcionaría si el contenedor tenía una relación de aspecto fija, pero no la tiene.
Th3S4mur41
Renunciar a la altura máxima tampoco funciona, eso solo significaría mover el problema de desbordamiento al padre
Th3S4mur41
Con esos 3 requisitos, no parece que haya una solución, es decir, usar solo CSS. ¿Está usando Javascript para calcular el ancho SVG apropiado fuera de cuestión?
H. Figueiredo
Eso es lo que está haciendo mi solución actual que me gustaría evitar;)
Th3S4mur41
Espero que la relación de aspecto ayude a resolver esto, pero ni siquiera estoy seguro de eso. drafts.csswg.org/css-sizing-4/#aspect-ratio
Th3S4mur41
0

Agregue al contenedor altura y ancho específicos junto con max-height y max-width.

Las unidades vw y vh se pueden usar para hacer este diseño más flexible.

  max-height: 100px;
  max-width: 200px;
  width: 100vw;
  height: 100vh;

De esta forma, es posible establecer max-width: 100%; max-height: 100%;el svg y no se superpondrá.


-EDITAR-

Dado que el cuadro de vista svg ya está renderizado con la relación de aspecto correcta, es seguro mover el fondo svg dentro del mismo svg para obtener el resultado esperado: una etiqueta de ciclo hará el truco.

¿Es posible imitar el radio del borde con otra etiqueta circular dentro de una máscara?

Todos los demás elementos del svg deben colocarse dentro de un grupo enmascarado. es decir:<g mask="url(#mask)">

.container {
  display: grid;
  background-color: greenyellow;
  margin: 5px;
  min-height: 10px;
  min-width: 10px;
}

.portrait {
  max-height: 100px;
  max-width: 200px;
  width: 100vw;
  height: 100vh;
}

.landscape {
  max-height: 200px;
  max-width: 100px;
  width: 100vw;
  height: 100vh;
}

.aspect-ratio {
  grid-column: 1;
  grid-row: 1;
  align-self: center;
  justify-self: center;
  max-width: 100%;
  max-height: 100%;
}

.aspect-ratio .bkg{
  fill: deeppink;
}
<div class="container landscape">
  <svg class="aspect-ratio" viewBox="0 0 1 1">
    <mask id="mask">
      <circle cx=".5" cy=".5" r=".5" fill="#fff"/>
    </mask>
    <circle cx=".5" cy=".5" r=".5" class="bkg"/>
    <g  mask="url(#mask)">

    </g>
  </svg>
</div>

<div class="container portrait">
  <svg class="aspect-ratio"viewBox="0 0 1 1">
    <mask id="mask">
      <circle cx=".5" cy=".5" r=".5" fill="#fff"/>
    </mask>
    <circle cx=".5" cy=".5" r=".5" class="bkg"/>
    <g  mask="url(#mask)">

    </g>
  </svg>
</div>

thetont
fuente
1
La pregunta especificaba "¿Hay alguna manera de que también se adapte a la altura mientras se mantiene la relación de aspecto?", Que su ejemplo no. Es una forma de forzar el SVG dentro del contenedor, pero no producirá el resultado deseado. Es lo mismo que el último ejemplo de mi respuesta donde el svg se aprieta para caber en el contenedor
H. Figueiredo
La explicación puede ser un poco confusa, pero con la solución sugerida, la altura del svg se adapta a la altura del contenedor.
Thetont
¡Pero no mantiene la relación de aspecto SVG ! ¿Has visto el resultado deseado que publicó el OP?
H. Figueiredo
Oh, wow ... Acabo de comprobarlo con Chrome y ahora entiendo lo que estás diciendo. Lo codifiqué en Firefox y con ese navegador funciona según lo previsto, no esperaba que el resultado fuera tan diferente. Mi mal
thetont
Ese es en realidad un comportamiento bastante interesante, supongo que Firefox y Chrome computan de manera diferente en un 100%. De todos modos, todos cometemos errores, de lo contrario SO no existiría: D
H. Figueiredo