¿Cómo alinear verticalmente todo el texto en CSS?


12

El problema parece ser que ciertas letras como g, y, q, etc, que tienen una cola que se inclina hacia abajo, no permiten el centrado vertical. Aquí hay una imagen para mostrar el problema una.

Los caracteres en el cuadro verde son básicamente perfectos, ya que no tienen cola hacia abajo. Los que están en el cuadro rojo demuestran el problema.

Me gustaría que todos los personajes estén perfectamente centrados verticalmente. En la imagen, los personajes con una cola hacia abajo no están centrados verticalmente. ¿Es esto posible rectificar?

Aquí está el violín que demuestra el problema en su totalidad .

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


1
Esta es una pregunta interesante. La respuesta podría estar aquí: iamvdo.me/en/blog/…
dwjohnston

2
en este caso necesitas definir cuál es el centro . Para mí, la y está realmente centrada y la A quizás no. no hablemos cuando tenemos una familia de fuentes diferente, la alineación empeorará aún más
Temani Afif

1
Así es como funcionan las letras. Están alineados, porque ven yy oen la parte gestán en la misma línea que el punto más bajo para las letras mayúsculas. Con su lógica, Å, Ä, Ö estarían alineados como A y O, pero no pueden estarlo. Si desea hacer algo especial al respecto, debe usar javascript para verificar si es una pequeña capitalización y luego empujar el carácter hacia arriba unos pocos caracteres.
Rickard Elimää

1
Tengo curiosidad si hay una respuesta útil aquí. El problema parece ser que estos están realmente centrados. es decir. digamos que puedes mover la y y la g hacia arriba, ¿qué tal si tuvieras una a minúscula? ¿Cómo debería mostrarse?
dwjohnston

2
Abra su fuente en cualquier editor de fuentes. Edita cada personaje hasta que estés contento con lo que ves como "centrado" . Guarde y use como su nueva familia de fuentes. No debe tomar más de 15 min. No veo otra forma sensata además de SVG u otras trickeries que usan canvas & co. Pero pensaré en esta pregunta.
Roko C. Buljan

Respuestas:


4

Aquí está mi solución usando JS. La idea es transformar el elemento en una imagen para obtener sus datos en píxeles y luego recorrerlos para encontrar la parte superior e inferior de cada carácter y aplicar una traducción para corregir la alineación. Esto funcionará con propiedades de fuente dinámica.

El código no está optimizado pero destaca la idea principal:

ACTUALIZAR

Aquí hay una primera optimización del 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>

Estoy usando el complemento dom-to-image para esto.


Un poco lento, pero hasta ahora es la primera solución que funciona de manera consistente. (Con la advertencia de que el centrado técnicamente se ve raro.)
Brilliand el

@Brilliand, explícitamente, solía Âmostrar que su centrado no es óptimo, como comenté en su respuesta;) Probablemente cambiará de opinión después de ver esto y aceptará la forma en que funciona la fuente
Temani Afif

Esto se ve bastante bien. Creo que usaré esto, si puedo hacerlo más eficiente. Actualmente parece un poco lento.
Chron Bag el

@ChronBag sí, puedes trabajar en el código y optimizarlo, hay espacio para esto. No lo hice, ya que me lleva mucho tiempo y no es realmente el propósito de la pregunta. Quería centrarme en la idea principal. Probablemente lo haga más tarde por cierto.
Temani Afif

@ChronBag agregó una optimización, un poco más rápido, supongo.
Temani Afif

1

Quizás haya una mejor respuesta, pero parece que la única forma de hacerlo es aplicar manualmente diferentes estilos dependiendo de si es uno de:

  • Letra mayúscula
  • Minúscula con cola
  • Minúscula con un tallo
  • Minúscula con ninguno

Ahora tenga en cuenta que, en mi opinión, las alturas relativas de colas y tallos creo que está definido por la fuente. No estoy seguro de si hay una manera de acceder a eso programáticamente, por lo que es posible que deba ajustar estos valores con la fuente.

Tenga en cuenta también que esta solución no funcionaría para admitir varios idiomas, ya que necesitaría definir en qué categoría encaja cada carácter individual en docenas 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>


Esperaba evitar algo como esto, pero puede ser la única solución. Si no aparece nada más, probablemente usaré esto. Gracias
Chron Bag

2
Si la letra puede ser cualquier letra unicode (de cualquier alfabeto), entonces esto se vuelve inviable.
Brilliand

Olvidas el ÂËyâë
Temani Afif

Ah sí, buen punto @Brilliand Mis usuarios podrán usar cualquier texto Unicode para sus nombres para mostrar, por lo que esta solución es aún más frágil.
Bolso Chron

0

¡Esta es una situación difícil!

Por lo que puedo decir, esto será más difícil de hacer de forma nativa escalable (es decir %, vwo vhvalores en lugar de pxo em). Si necesita hacer que esto se vea bonito en dispositivos móviles o tabletas, considere usar mi solución con @mediapuntos de interrupción.

Mi solución esencialmente detecta si es un elemento en minúscula con una cola y agrega una clase para compensar la altura para compensar la cola.

En mis pruebas, no parecía que era necesario ningún controlador adicional de capital de las letras o minúsculas letras sin cola . Siéntase libre de corregirme si me equivoco.

Hay un JSFiddle si desea perder el tiempo y cambiar / probar esta solución.

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>

Déjame saber tus pensamientos y si me he perdido algo. ¡Sería genial obtener una solución final para esto, ya que he tenido problemas similares en el pasado!


Esto parece ser similar a la solución de dwjohnston, y está sujeto a las mismas trampas que la suya. Sin embargo, se agradece, y podría terminar yendo con algo en este sentido si no aparece nada más ideal.
Bolsa cronológica

Supongo que como último recurso, podría seguir el camino del recuento de píxeles, como se ve aquí, mediante el cual detectaría su altura real en píxeles y luego aplicaría el desplazamiento apropiado.
EGC

1
Sí, mencioné esa idea en los comentarios al OP. Tal vez ese es el camino a seguir.
Bolso Chron

Perdón por notar tu idea otra vez ... demasiados comentarios, ¡obviamente la perdí!
EGC

-1

Probablemente necesite una clase auxiliar para esto, de modo que pueda traducir las letras minúsculas más que las mayúsculas. Un script simple puede poner fácilmente estas clases de ayuda automáticamente.

Espero que esto resuelva el problema para ti :)

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


tu yno está centrado. Tweek un poco más!
Roko C. Buljan

Tampoco sé de antemano cuáles serán las letras.
Chron Bag el

Entiendo eso, pero un script que verifica si la letra es mayúscula o minúscula y pone una clase auxiliar en el HTML basado en eso se solucionará fácilmente :)
alguien el

@RagnaRoaldset Creo que no quiere decir mayúsculas o minúsculas. Él quiso decir que hay minúsculas pero diferentes formas de personaje. Compare oy yobtendrá eso. Los caracteres con colas crean el problema en primer lugar (mencionado en la publicación).
Anna

1
Siempre puede crear una matriz para todas las letras con colas, y recorrerla para verificar si la letra en el div coincide antes de aplicar la clase :) Pero lo entiendo, solo traté de encontrar una solución :) espero que a alguien se le ocurra :)
alguien
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.