O círculo SVG deve se adaptar à altura da grade CSS sem sobrepor

8

No exemplo a seguir, o círculo se adapta à largura da grade e mantém sua proporção. À medida que a largura do recipiente diminui, os círculos diminuem com ele ...

Porém, quando a altura é menor que a largura (segunda caixa), o círculo não diminui as sobreposições fora da grade. Existe uma maneira de adaptá-lo à altura, mantendo a proporção?

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

O resultado deve ficar assim: insira a descrição da imagem aqui

Th3S4mur41
fonte
Você tem a possibilidade de editar o código svg?
thetont 21/01

Respostas:

8

Depois de mexer algum tempo com esse problema, percebi que isso não é um problema com o layout da grade.

Como você nunca define altura / altura máxima ou largura / largura máxima para o seu SVG, o que está sendo calculado é width:autoeheight:auto

Nota: Eu trago max-height / max-width, pois eles teriam prioridade sobre os valores de largura / altura .


Agora, por que o layout da paisagem funciona e o retrato não funciona? Porque a largura está "priorizando" sua altura.

E é por isso que, no layout do bloco, uma altura automática é baseada na altura total dos descendentes e uma largura automática é baseada na largura do bloco que o contém.

Fonte - Por que width: auto se comporta de maneira diferente de height: auto?

Isso significa que seu SVG está retirando o widthcontêiner e os heightdescendentes.

No seu exemplo, seu SVG não tem descendentes, mas tem um pai com uma largura máxima definida . Mas o que isso significa para a altura do SVG ?

O SVG está calculando sua altura através da razão intrísica:

Se a 'viewBox' no elemento 'svg' estiver especificada corretamente:

  1. deixe viewbox ser a viewbox definida pelo atributo 'viewBox' no elemento 'svg'
  2. retornar viewbox.width / viewbox.height

Fonte

E sua proporção intrísica é 1/1 = 1

Então você está forçando sua height=width


As soluções)

A solução está configurando a widthou a marginpara o seu SVG, dependendo da proporção da sua div principal.

No exemplo que você deu, você tem uma proporção de 2: 1, então seu SVG deve ter width:50%ou margin: 0 25%.

Isso significa que, se você deseja definir uma proporção diferente, deve ajustar em conformidade (veja retrato-1-3 no código abaixo).

A definição de uma regra CSS para cada proporção pode ser evitada se heightnão houver uma restrição, pois você pode definir a widthpara o SVG e o contêiner, permitindo que os elementos ajustem heightpara manter a proporção.

Também adicionei o exemplo final, se alguém não se preocupa em manter a proporção, mas precisa conter o SVG em um conjunto heighte em um widthcontêiner.

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

Note que eu não sou muito proficiente trabalhando com SVGs e essa resposta é resultado de pesquisas e muita experimentação! Ficaria feliz em receber quaisquer ajustes e / ou correções na minha resposta.

H. Figueiredo
fonte
Obrigado pela explicação detalhada ... Devo notar que sua pesquisa infelizmente retornou os mesmos resultados que os meus. Parece não haver solução para meus requisitos: 1. A taxa de proporção SVG é fixa. 2. a proporção de aspecto do contêiner é variável / desconhecida 3. o tamanho do contêiner é limitado por altura e largura, dependendo do ambiente (por exemplo, tamanho da janela do navegador) A solução proposta, configurando a largura do SVG para uma determinada porcentagem do contêiner, funcionaria se o contêiner tinha proporção fixa, mas não.
Th3S4mur41
Desistir da altura máxima também não funciona, isso significaria apenas mover a questão do estouro para o pai
Th3S4mur41
Com esses três requisitos, parece que não há uma solução, ou seja, usando apenas CSS. Está usando Javascript para calcular a largura apropriada de SVG fora de questão?
H. Figueiredo
É isso que minha solução atual está fazendo, que eu gostaria de evitar;)
Th3S4mur41
Espero que a proporção ajude a resolver isso, mas não tenho certeza disso. drafts.csswg.org/css-sizing-4/#aspect-ratio
Th3S4mur41
0

Adicione ao recipiente altura e largura específicas ao lado de altura e largura máx.

as unidades vw e vh podem ser usadas para tornar esse layout mais flexível.

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

Dessa forma, é possível definir max-width: 100%; max-height: 100%;o svg e ele não se sobrepõe.


-EDITAR-

Como a caixa de exibição svg já é renderizada com a proporção correta, é seguro mover o fundo svg dentro do próprio svg para obter o resultado esperado: uma tag cicle fará o truque.

É possível imitar o raio da borda com outra marca de círculo dentro de uma máscara

Todos os outros elementos do svg devem ser colocados dentro de um grupo mascarado. ou seja:<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
fonte
1
A pergunta especificada "Existe uma maneira de adaptá-la à altura, mantendo a proporção", que o seu exemplo não. É uma maneira de forçar o SVG para dentro do contêiner, mas não produzirá o resultado pretendido. É o mesmo que o último exemplo da minha resposta em que o svg é compactado para caber no contêiner
H. Figueiredo
A explicação pode ser um pouco confusa, mas com a solução sugerida, a altura do svg se adapta à altura do contêiner.
thetont 21/01
Mas não mantém a proporção SVG ! Você viu o resultado desejado que o OP postou?
H. Figueiredo
Oh, uau ... Acabei de verificar com o Chrome e agora entendo o que você está dizendo. Eu o codifiquei no Firefox e, com esse navegador, funciona como pretendido, não esperava que o resultado fosse tão diferente. My bad
thetont
Na verdade, esse é um comportamento bastante interessante, acho que o Firefox e o Chrome calculam de maneira diferente para 100%? Enfim, todos cometemos erros, caso contrário, o SO não existiria: D
H. Figueiredo