【问题标题】:How to vertically align all text in CSS?如何垂直对齐CSS中的所有文本?
【发布时间】:2020-03-14 09:20:09
【问题描述】:

问题似乎是某些字母(例如 gyq 等)的尾部向下倾斜,不允许垂直居中。这是一个展示问题的图片

绿色方框中的字符基本上是完美的,因为它们没有向下的尾巴。红框内的部分说明了问题所在。

我希望所有字符都完全垂直居中。在图像中,尾部向下的字符不是垂直居中的。这可以纠正吗?

Here is the fiddle that demonstrates the problem in full.

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

【问题讨论】:

  • 这是一个有趣的问题。答案可能在这里:iamvdo.me/en/blog/…
  • 在这种情况下,您需要定义什么是 center。对我来说, y 实际上是居中的,而 A 可能不是。当我们有不同的字体系列时,我们不要说话,对齐会变得更糟
  • 这就是字母的工作原理。它们是对齐的,因为y 中的vg 中的o 部分与大写字母的最低点位于同一行。根据您的逻辑,Å、Ä、Ö 将与 A 和 O 一样对齐,但它们不能对齐。如果你想对它做一些特别的事情,你需要使用 javascript 检查它是否是一个小型大写字母,然后将字符向上推几个字符。
  • 我很好奇这里是否有有用的答案。问题似乎是这些确实是居中的。 IE。假设你可以将 y 和 g 向上移动,如果你有一个小写的 a 呢?那应该怎么显示呢?
  • 在任何字体编辑器中打开您的字体。编辑每个字符,直到您对所看到的 “居中” 感到满意为止。保存并用作您全新的字体系列。应该不超过 15 分钟。除了 SVG 或其他使用 canvas & co 的诡计之外,我没有看到其他理智的方法。但我会考虑这个问题。

标签: javascript html css vertical-alignment


【解决方案1】:

这是我使用 JS 的解决方案。这个想法是将元素转换为图像,以便将其数据作为像素获取,然后循环遍历它们以找到每个字符的顶部和底部,并应用平移来修复对齐。这将适用于动态字体属性。

代码没有优化,但突出了主要思想:

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

var fixes = [];


for (var i = 0; i < elems.length; i++) {
  var current = elems[i];
  domtoimage.toPixelData(current)
    .then(function(im) {
      /* Search for the top limit */
      var t = 0;
      for (var y = 0; y < current.scrollHeight; ++y) {
        for (var x = 0; x < current.scrollWidth; ++x) {
          var j = (4 * y * current.scrollHeight) + (4 * x);
          if (im[j] == 255 && im[j + 1] == 255 && im[j + 2] == 255) {
            t = y;
            break;
          }
        }
      }
      /* Search the bottom limit*/
      var b = 0;
      for (var y = (current.scrollHeight - 1); y >= 0; --y) {
        for (var x = (current.scrollWidth - 1); x >= 0; --x) {
          var j = (4 * y * current.scrollHeight) + (4 * x);
          if (im[j] == 255 && im[j + 1] == 255 && im[j + 2] == 255) {
            b = current.scrollHeight - y;
            break;
          }
        }
      }
      /* get the difference and apply a translation*/
      var diff = (b - t)/2;
      fixes.push(diff);
      /* we apply the translation when all are calculated*/
      if(fixes.length == elems.length) {
        for (var k = 0; k < elems.length; k++) {
          elems[k].querySelector('.character').style.transform = "translateY(" + fixes[k] + "px)";
        }
      }
    });
}
.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>

更新

这是代码的第一次优化:

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>

我正在为此使用dom-to-image 插件。

【讨论】:

  • 运行缓慢,但这是迄今为止第一个始终如一的解决方案。 (需要注意的是,以技术为中心的 Â 看起来很奇怪。)
  • @Brilliand 我明确使用Â 来表明他的居中不是最佳的,正如我在他的回答中评论的那样;)他可能会在看到这一点后改变主意并接受字体的工作方式跨度>
  • 这看起来不错。我想我会用这个,如果我能提高效率的话。目前似乎有点慢。
  • @ChronBag 是的,您可以处理代码并对其进行优化,这是有空间的。我没有这样做,因为这需要很多时间,而且这并不是问题的真正目的。我想专注于主要思想。顺便说一句,以后可能会这样做。
  • @ChronBag 添加了优化,我猜要快一点。
【解决方案2】:

也许有更好的答案,但听起来唯一的方法是手动应用不同的样式,具体取决于它是否是以下之一:

  • 大写字母
  • 带尾巴的小写字母
  • 带柄小写
  • 两者都小写

现在请注意,在我的理解中,我认为尾巴和茎的相对高度是由字体定义的。我不确定是否有办法以编程方式访问它 - 所以您可能需要使用字体调整这些值。

另请注意,此解决方案不适用于支持多种语言 - 因为您需要定义每个字符适合数十种不同字符集的类别。

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>

【讨论】:

  • 我希望避免这样的事情,但这可能是唯一的解决方案。如果没有其他事情发生,我可能会使用这个。谢谢
  • 如果字母可以是任何 unicode 字母(来自任何字母表),那么这将变得不可行。
  • 你忘了ÂËâë
  • 啊,好的,@Brilliand 我的用户将能够使用任何 unicode 文本作为他们的显示名称,所以这个解决方案更加脆弱。
【解决方案3】:

这是一个棘手的情况!

据我所知,这将是最难实现本机可扩展性的(即%vwvh 值,而不是 pxem)。如果您需要在移动设备或平板电脑上使其看起来更漂亮,请考虑使用我的解决方案与 @media 断点。

我的解决方案本质上检测它是否是一个小写元素有尾巴,并添加一个类来抵消高度以补偿尾巴。

在我的测试中,大写字母或小写字母没有尾巴似乎不需要任何额外的处理程序。如果我错了,请随时纠正我。

如果您想搞砸并更改/测试此解决方案,可以使用JSFiddle

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>

让我知道您的想法以及我是否遗漏了什么。获得最终解决方案会很酷,因为我自己过去也遇到过类似的问题!

【讨论】:

  • 这似乎与 dwjohnston 的解决方案相似,并受到与他相同的陷阱。尽管如此,还是值得赞赏的,如果没有更理想的情况出现,我最终可能会沿着这些思路走。
  • 我想作为最后的手段,你可以沿着像素计数的道路前进 - 如 here 所见,您将检测它的 实际高度(以像素为单位),然后应用适当的偏移量。
  • 是的,我在 OP 的 cmets 中提到了这个想法。也许这就是要走的路。
  • 很抱歉再次注意到你的想法..太多的cmets,显然错过了!
【解决方案4】:

你可能需要一个帮助类,这样你就可以翻译小写字母而不是大写字母。一个简单的脚本可以很容易地自动将这些辅助类放在上面。

希望这能解决你的问题:)

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

【讨论】:

  • 您的y 未居中。再多吃点!
  • 我也不知道这些字母会是什么。
  • 我明白这一点,但是一个检查字母是大写还是小写并在 HTML 上放置一个帮助类的脚本会很容易解决这个问题:)
  • @RagnaRoaldset 我认为他的意思不是大写或小写。他的意思是有小写但不同的字符形状。比较oy,你会明白的。带尾巴的字符首先产生了问题(在帖子中提到)。
  • 你总是可以为所有带尾巴的字母创建一个数组,并在应用类之前遍历它以检查 div 中的字母是否匹配 :) 但我明白了,只是想用一个解决方案:)希望有人想出它:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-13
  • 2013-05-25
  • 2017-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多