Como alinhar verticalmente todo o texto em CSS?

12

A questão parece ser que certas letras, como g, y, q, etc. que têm uma cauda que se inclina para baixo, não permitem a centragem vertical. Aqui está uma imagem para mostrar o problema uma.

Os caracteres na caixa verde são basicamente perfeitos, pois não têm cauda descendente. Aqueles na caixa vermelha demonstram o problema.

Eu gostaria que todos os personagens estivessem perfeitamente centralizados na vertical. Na imagem, os caracteres com uma cauda descendente não são centralizados verticalmente. É possível corrigir isso?

Aqui está o violino que demonstra o problema por completo .

.avatar {
    border-radius: 50%;
    display: inline-block;
    text-align: center;
    width: 125px;
    height: 125px;
    font-size: 60px;
    background-color: rgb(81, 75, 93);
    font-family: "Segoe UI";
    margin-bottom: 10px;
}

.character {
    position: relative;
    top: 50%;
    transform: translateY(-50%);
    line-height: 100%;
    color: #fff;
}
<div class="avatar">
  <div class="character">W</div>
</div>

<div class="avatar">
  <div class="character">y</div>
</div>

Chron Bag
fonte
11
Esta é uma pergunta interessante. A resposta pode estar aqui: iamvdo.me/pt/blog/…
dwjohnston 18/11/19
2
neste caso, você precisa definir qual é o centro . Para mim, o y está realmente centrado e o A talvez não. não vamos conversar quando tivermos uma família de fontes diferente, o alinhamento ficará ainda pior #
Temani Afif 18/11/19
11
É assim que as letras funcionam. Eles estão alinhados, porque vna ye oparte na gestão na mesma linha que o ponto mais baixo para as letras maiúsculas. Com a sua lógica, Å, Ä, Ö seria alinhado como A e O, mas não podem ser. Se você quiser fazer algo especial a respeito, precisará usar o javascript para verificar se é uma letra minúscula e depois empurrar o personagem para alguns caracteres.
Rickard Elimää 18/11/19
11
Estou curioso para saber se há uma resposta útil aqui. O problema parece ser o fato de que estes realmente estão centralizados. ie digamos que você possa mover ey e g para cima, e se você tivesse um a minúsculo? como isso deve ser exibido?
dwjohnston
2
Abra sua fonte em qualquer editor de fontes. Edite todos os personagens até ficar satisfeito com o que vê como "centralizado" . Salve e use como sua nova família de fontes. Não deve demorar mais de 15 minutos. Não vejo outra maneira sã além de SVG ou outros truques usando canvas & co. Mas vou pensar sobre esta questão.
Roko C. Buljan

Respostas:

4

Aqui está a minha solução usando JS. A idéia é transformar o elemento em uma imagem, a fim de obter seus dados como pixel, percorrê-los para encontrar as partes superior e inferior de cada caractere e aplicar uma tradução para corrigir o alinhamento. Isso funcionará com propriedades de fonte dinâmica.

O código não é otimizado, mas destaca a ideia principal:

ATUALIZAR

Aqui está uma primeira otimização do código:

var elems = document.querySelectorAll(".avatar");
var k = 0;

for (var i = 0; i < elems.length; i++) {
  domtoimage.toPixelData(elems[i])
    .then(function(im) {
     var l = im.length;
      /* Search for the top limit */
      var t = 0;
      for (var j = 0; j < l; j+=4) {
          if (im[j+1] == 255) { /* Since we know the colors, we can only test the G composant */
            t = Math.ceil((j/4)/125);
            break;
          }
      }
      /* Search the bottom limit*/
      var b = 0;
      for (var j = l - 1; j >= 0; j-=4) {
          if (im[j+1] == 255) {
            b = 125 - Math.ceil((j/4)/125);
            break;
          }
      }
      /* get the difference and apply a translation*/
      elems[k].querySelector('.character').style.transform = "translateY(" + (b - t)/2 + "px)";
      k++;
    });
}
.avatar {
  border-radius: 50%;
  display: inline-flex;
  vertical-align:top;
  justify-content: center;
  align-items: center;
  width: 125px;
  height: 125px;
  font-size: 60px;
  background: 
    linear-gradient(red,red) center/100% 1px no-repeat,
    rgb(81, 75, 93);
  font-family: "Segoe UI";
  margin-bottom: 10px;
}

.character {
  color: #fff;
}
<script type="text/javascript" src="https://css-challenges.com/wp-content/themes/ronneby_child/js/dom-to-image.js"></script>
<div class="avatar">
  <div class="character">W</div>
</div>

<div class="avatar">
  <div class="character">y</div>
</div>

<div class="avatar">
  <div class="character" style="font-size:35px">a</div>
</div>

<div class="avatar">
  <div class="character" style="font-size:25px">2</div>
</div>
<div class="avatar">
  <div class="character">o</div>
</div>
<div class="avatar">
  <div class="character">|</div>
</div>
<div class="avatar">
  <div class="character">@</div>
</div>
<div class="avatar">
  <div class="character">Â</div>
</div>

<div class="avatar">
  <div class="character" style="font-family:arial">Q</div>
</div>
<div class="avatar">
  <div class="character">~</div>
</div>
<div class="avatar">
  <div class="character">8</div>
</div>

<div class="avatar">
  <div class="character">ä</div>
</div>
<div class="avatar">
  <div class="character">ç</div>
</div>

<div class="avatar">
  <div class="character">$</div>
</div>

<div class="avatar">
  <div class="character">></div>
</div>
<div class="avatar">
  <div class="character">%</div>
</div>

Estou usando o plugin dom-to-image para isso.

Temani Afif
fonte
Um ácaro de execução lenta, mas esta é a primeira solução de trabalho consistente até agora. (Com a ressalva de que o A-tecnicamente centrado parece estranho.)
Brilliand
@Brilliand I explicitamente utilizado Âpara mostrar que sua centralização não é o ideal como eu comentei em sua resposta;) Ele provavelmente vai mudar de idéia depois de ver isso e aceitar as obras de fontes maneira
temanitas Afif
Isso parece muito bom. Acho que vou usar isso, se puder torná-lo mais eficiente. Atualmente, parece um pouco lento.
Chron Bag
@ChronBag sim, você pode trabalhar no código e otimizá-lo, há espaço para isso. Eu não fiz isso, pois levo muito tempo e esse não é realmente o objetivo da pergunta. Eu queria me concentrar na idéia principal. Provavelmente fará mais tarde a propósito.
Temani Afif 19/11/19
O @ChronBag adicionou uma otimização, um pouco mais rápido, eu acho.
Temani Afif 19/11/19
1

Talvez exista uma resposta melhor, mas parece que a única maneira é aplicar manualmente estilos diferentes, dependendo se é um dos seguintes:

  • Letra maiúscula
  • Minúsculas com cauda
  • Minúsculas com uma haste
  • Minúsculas sem

Agora observe que, no meu entendimento, as alturas relativas de caudas e caules que eu acho que são definidas pela fonte. Não tenho certeza se existe uma maneira de acessar isso programaticamente - portanto, você pode precisar ajustar esses valores com a fonte.

Observe também que essa solução não funcionaria no suporte a vários idiomas - pois você precisaria definir em qual categoria cada caractere se encaixa em dezenas de conjuntos de caracteres diferentes.

const letters = ['a', 'b', 'y', 'X', 'c', 'y', 'A', 'B', 'Y']; 

function getAdditionalClass(char){
    //To do - fill arrays with the rest of the appropriate letters
    if (['y', 'g'].includes(char)) {
        return "tail"; 
    }
    if (['b', 'd'].includes(char)) {
        return "stalk"; 
    }
    
    if (['a', 'c'].includes(char)) {
        return "small"; 
    }
    
    return "capital"; 
}

letters.forEach(v => {
  const avatar = document.createElement("div"); 
  avatar.className = "avatar"; 
  const character = document.createElement("div");
  character.textContent = v; 
  character.className = `character ${getAdditionalClass(v)}`; 
  
  avatar.appendChild(character); 
  
  const root = document.getElementById("root"); 
  
  root.appendChild(avatar); 
  
});
.avatar {
    border-radius: 50%;
    display: block;
    text-align: center;
    width: 125px;
    height: 125px;
    font-size: 60px;
    background-color: rgb(81, 75, 93);
    font-family: "Segoe UI";
    margin-bottom: 10px;
}

.character {
    position: relative;
    transform: translateY(-50%);
    line-height: 100%;
    color: #fff;
}


.small {
    top: 45%; 
}

.stalk {
    top: 50%;
}

.tail {
    top: 41%;
}

.capital {
    top: 50%;
}

#root {
    display: flex; 
    flex-flow: row wrap; 
}
<div id = "root">

</div>

Dwjohnston
fonte
Eu esperava evitar algo assim, mas pode ser a única solução. Se nada mais acontecer, provavelmente vou usar isso. Obrigado
Chron Bag
2
Se a letra puder ser qualquer letra unicode (de qualquer alfabeto), isso se torna impraticável.
Brilliand
Você esquece o ÂËeâë
Temani Afif 18/11/19
Ah, sim, bom ponto @Brilliand Meus usuários poderão usar qualquer texto unicode para seus nomes de exibição, portanto, esta solução é ainda mais frágil.
quer
0

Esta é uma situação complicada!

Do que eu posso dizer, isso será mais difícil fazer escalável nativamente (ie %, vwou vhvalores em vez de pxou em). Se você precisar fazer isso parecer bonito no celular ou tablet, considere usar minha solução com @mediapontos de interrupção.

Minha solução basicamente detecta se é um elemento minúsculo com uma cauda e adiciona uma classe para compensar a altura para compensar a cauda.

Em meus testes, ele não parece que qualquer manipuladores adicionais foram necessários para capital de letras ou minúsculas letras sem uma cauda . Fique à vontade para me corrigir se eu estiver errado.

Existe um JSFiddle se você quiser mexer e alterar / testar esta solução.

var circles = document.getElementsByClassName('circle');
var tails = ['q', 'y', 'p', 'g', 'j'] ;

Array.from(circles).forEach(render);
  
function render(element) { 		
    if(element.innerText == element.innerText.toLowerCase() &&
    	tails.includes(element.innerText)) {
    	element.className += " scale";
    }
}
.circle {
  height: 150px;
  width: 150px;
  background-color: #bbb;
  border-radius: 50%;
  display: inline-block;
  text-align: center;
  vertical-align: middle;
  line-height: 150px;
  font-size: 50px;
}

.scale {
  line-height: 135px;
}
<div>
  <div class="circle">W</div>
  <div class="circle">X</div>
</div>
<div>
  <div class="circle">y</div>
  <div class="circle">t</div>
</div>

Deixe-me saber seus pensamentos e se eu perdi alguma coisa. Seria legal obter uma solução final para isso, pois eu já tive problemas semelhantes no passado!

EGC
fonte
Isso parece ser semelhante à solução de dwjohnston e sujeito às mesmas armadilhas que a dele. No entanto, é apreciado, e eu posso acabar indo com algo nesse sentido, se nada mais ideal aparecer.
quer
Suponho que, como último recurso, você possa seguir o caminho da contagem de pixels - como visto aqui, onde você detectaria sua altura real em pixels e aplicaria o deslocamento apropriado.
EGC
11
Sim, eu mencionei essa idéia nos comentários do OP. Talvez seja esse o caminho a seguir.
Chron Bag
Desculpe por anotar sua idéia novamente .. muitos comentários, obviamente, perdi!
EGC
-1

Você provavelmente precisaria de uma classe auxiliar para isso, para poder traduzir mais as letras minúsculas do que as maiúsculas. Um script simples pode facilmente colocar essas classes auxiliares automaticamente.

Espero que isso resolva o problema para você :)

.avatar {
    border-radius: 50%;
    display: block;
    text-align: center;
    width: 125px;
    height: 125px;
    font-size: 60px;
    background-color: rgb(81, 75, 93);
    font-family: "Segoe UI";
    margin-bottom: 10px;
}

.character {
    position: relative;
    top: 50%;
    line-height: 100%;
    color: #fff;
}
.character-lowercase {
  transform: translateY(-60%);
}
.character-capital {
  transform: translateY(-50%);
}
<div class="avatar">
  <div class="character character-capital">W</div>
</div>

<div class="avatar">
  <div class="character character-lowercase">y</div>
</div>

Ragna Roaldset
fonte
o seu ynão está centrado. Tweek um pouco mais!
Roko C. Buljan
Também não sei antecipadamente quais serão as letras.
quer
Eu entendo isso, mas um script que verifica se a letra é maiúsculo ou minúsculo e coloca uma helperClass no HTML baseado em que vai corrigir isso facilmente :)
Ragna Roaldset
@RagnaRoaldset Acho que ele não quer dizer maiúsculas ou minúsculas. Ele quis dizer que existe uma forma minúscula, mas diferente, de caráter. Compare oe ye você vai ter isso. Os caracteres com rabos criam o problema em primeiro lugar (mencionado no post).
Anna
11
Você sempre pode criar uma matriz para todas as letras com caudas e percorrê-la para verificar se a letra na div corresponde antes de aplicar a classe :) Mas entendi, apenas tentei vir com uma solução :) espero que alguém venha com
Ragna Roaldset