在Safari中,文本的位置与其他浏览器不同。

10

在Safari中,文本的位置与其他浏览器不同。这是有原因的吗?是否有样式可以确保它符合精确的测量标准。

* {
  margin: 0;
  padding: 0;
}
#btn_signup {
  opacity: 1;
  position: absolute;
  width: 185px;
  height: 50px;
  left: 10px;
  top: 10px;
  overflow: visible;
}
#Rectangle_1 {
  opacity: 1;
  fill: transparent;
  stroke: rgb(67, 66, 93);
  stroke-width: 1px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}
.Rectangle_1 {
  position: absolute;
  overflow: visible;
  width: 185px;
  height: 50px;
  left: 0px;
  top: 0px;
}
#Sign_up {
  opacity: 1;
  position: absolute;
  left: 58px;
  top: 16px;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  overflow: visible;
  width: 63px;
  white-space: nowrap;
  text-align: center;
}
#Path_1 {
  opacity: 1;
  fill: rgba(0,0,0,0);
  stroke: rgb(112, 112, 112);
  stroke-width: 0.5px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}
.Path_1 {
  overflow: visible;
  position: absolute;
  top: 16.5px;
  left: 1px;
  width: 183.5px;
  height: 0.5px;
  transform: matrix(1,0,0,1,0,0);
}
<div id="A5">
  <div id="btn_signup">
    <svg class="Rectangle_1">
      <rect id="Rectangle_1" rx="4" ry="4" x="0" y="0" width="185" height="50"></rect>
    </svg>
    <div id="Sign_up">
      <span style="font-family:Helvetica;font-style:normal;font-weight:normal;font-size:18px;color:rgba(77,79,92,1);display:inherit;">Sign up</span>
    </div>
    <svg viewBox="12.5 0 183.5 0.5" class="Path_1">
      <path id="Path_1" d="M 12.5 0 L 196 0"></path>
    </svg>
  </div>
</div>

在Firefox中,文本与行齐平。在Safari中,存在几个像素的间隙,如下图所示:
enter image description here
我该如何去掉这个间隙?
增加了悬赏。如果需要更多明确,请提问。
更新:
感谢所有关于垂直居中的建议。我的一半情况可以通过垂直居中来解决,但其余情况是顶部对齐的情况。
看起来我想要的是将文本内容(内容的边界框处于大写字母高度)与文本行的顶部对齐,如图片所示。
资源:
看起来这是跨平台的常见问题。
Cap height and x-height metrics are inaccurate
Vertical Metrics

也许需要不同的 line-height;尝试明确设置它。 - Mr Lister
1
奇怪。我想不到其他的,而且我这里没有Safari可以测试,对不起。给你一个赞替代吧。 - Mr Lister
1
要对齐字体,您需要阅读其度量标准,以下是一种可能的方法:https://stackoverflow.com/questions/38242098/how-to-measure-the-font-metrics-in-javascript-is-there-any-api-available-for-te - Asons
1
更多阅读:https://dev59.com/oFgQ5IYBdhLWcg3w6IOg#42582978 - Asons
1
请注意,在您的评论中添加我的别名,这样我就会收到通知。 - Asons
显示剩余11条评论
5个回答

5
请看这个问题和答案:WebKit vs Mozilla vertical alignment of font glyphs in box 基本上,CSS允许你玩弄文本元素的位置,但实际的字母定位与元素是由字体和浏览器决定的。你无法直接修改它,这意味着在理论上,你不能确定字母本身的实际位置。
CSS给你间接的访问权限,例如使用vertical-align,你可以将文本与父文本对齐。因此,根据值的不同,元素将相对于实际字母定位。例如:

div {
  font-size: 20px;
  line-height: 1.2;
  border-top: solid lightgray 1px;
  font-family: arial;
}

div span {
  display: inline-block;
  background-color: lightblue;
  border-top: solid red 1px;
}

.topalign span {
  vertical-align: top;
}

.bottomalign span {
  vertical-align: bottom;
}
<div class="topalign">
  <span style="font-family: 'helvetica neue'; ">Helvetica N</span>
  <span style="font-family: 'arial'; font-size: 30px">Arial</span>
  <span style="font-family: 'Times new roman';  ">Times NR</span>
  <span style="font-family: 'Tahoma'; font-size: 15px;">Tahoma</span>
</div>
<div class="bottomalign">
  <span style="font-family: 'helvetica neue'; ">Helvetica N</span>
  <span style="font-family: 'arial'; font-size: 30px">Arial</span>
  <span style="font-family: 'Times new roman';  ">Times NR</span>
  <span style="font-family: 'Tahoma'; font-size: 15px;">Tahoma</span>
</div>

您也可以尝试使用行高进行调整,这会改变文本元素的高度而不改变字体大小,这意味着您可以在一定程度上控制字母的位置。但需要注意的是,不同的字体/浏览器渲染效果不同,因此不能精确地定位字母。例如:

div {
  font-size: 30px;
  line-height: 1.2;
  border-top: solid lightgray 1px;
  font-family: arial;
}

div span {
  display: inline-block;
  background-color: lightblue;
  border-top: solid red 1px;
}

.topalign span {
  vertical-align: top;
}
<div class="topalign">
  <span style="font-family: 'helvetica neue'; ">Helvetica N</span>
  <span style="font-family: 'arial'; line-height: 1.5;">Arial</span>
  <span style="font-family: 'Times new roman';  line-height: 0.5;">Times NR</span>
  <span style="font-family: 'Tahoma'; ">Tahoma</span>
</div>

理论上来说,您无法精确地定义您想要的实际字母位置。它不能明确,因此可能会导致浏览器之间的不一致。

话虽如此,您提出的问题似乎是Firefox与Helvetica字体的bug。 Helvetica的字母定位与其他字体或其他浏览器不一致。当您与Helvetica Neue或Arial进行比较时,这尤其明显,这些字体应该更或多或少相同。请参见:

div {
  font-size: 30px;
  line-height: 1.2;
  border-top: solid lightgray 1px;
  font-family: arial;
}

div span {
  display: inline-block;
  background-color: lightblue;
  border-top: solid red 1px;
}

.topalign span {
  vertical-align: top;
}
<div class="topalign">
  <span style="font-family: 'helvetica';  ">Helvetica </span>
  <span style="font-family: 'helvetica neue'; ">Helvetica N</span>
  <span style="font-family: 'arial'; ">Arial</span>
</div>

即使你不能明确地说明字母的位置,但你仍可以期望某种程度的一致性。通常,上下间距会相对均衡,保持正常和连贯状态。它可能因字体和浏览器而异,但在Firefox中呈现Helvetica的方式似乎有点像是个bug(至少对我来说是这样,也许有原因,但我不知道是什么)。
因此,如果您可以将Helvetica替换为Helvetica Neue,则可以通过调整行高来定位字母与元素之间的位置,从而实现您想要的效果。例如,通常的行高约为0.75,可以与大写字母的顶部和底部对齐,这意味着通过定位元素,您可以定位字母。就像下面这个例子:

* {
  margin: 0;
  padding: 0;
}
#btn_signup {
  opacity: 1;
  position: absolute;
  width: 185px;
  height: 50px;
  left: 10px;
  top: 10px;
  overflow: visible;
}
#Rectangle_1 {
  opacity: 1;
  fill: transparent;
  stroke: rgb(67, 66, 93);
  stroke-width: 1px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}
.Rectangle_1 {
  position: absolute;
  overflow: visible;
  width: 185px;
  height: 50px;
  left: 0px;
  top: 0px;
}
#Sign_up {
  opacity: 1;
  position: absolute;
  left: 58px;
  top: 16px;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  overflow: visible;
  width: 63px;
  white-space: nowrap;
  text-align: center;
}
#Path_1 {
  opacity: 1;
  fill: rgba(0,0,0,0);
  stroke: rgb(112, 112, 112);
  stroke-width: 0.5px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}
.Path_1 {
  overflow: visible;
  position: absolute;
  top: 16.5px;
  left: 1px;
  width: 183.5px;
  height: 0.5px;
  transform: matrix(1,0,0,1,0,0);
}
<div id="A5">
  <div id="btn_signup">
    <svg class="Rectangle_1">
      <rect id="Rectangle_1" rx="4" ry="4" x="0" y="0" width="185" height="50"></rect>
    </svg>
    <div id="Sign_up">
      <span style="font-family:Helvetica neue;font-style:normal;font-weight:normal;font-size:18px;color:rgba(77,79,92,1);display:inherit;line-height: 0.75;">Sign up</span>
    </div>
    <svg viewBox="12.5 0 183.5 0.5" class="Path_1">
      <path id="Path_1" d="M 12.5 0 L 196 0"></path>
    </svg>
  </div>
</div>


那么你的意思是我们需要一种新的样式,确保文本的边界框,在大写字母高度处与其容器元素的边缘对齐,以确保在各种浏览器和字体中看起来一致?你会给这个新样式起什么名字? - 1.21 gigawatts
确切地说,似乎没有任何工具或标准可以精确定位字母。渲染的一部分依赖于字体信息,而这些信息并未公开 - 至少我是这样理解的。也许有一天,类似于字形对齐规则的东西可以让我们将字母与父元素的顶部、底部或中间对齐。那将解决您提到的问题。但这可能会引起许多其他问题... - Julien Grégoire

2
不同的浏览器对小数点位置处理方式不同。处理小数像素不在CSS规范中,因此即使现在一切都定位正确,在浏览器更新后可能会再次出现问题。如果您删除所有小数像素值并添加 line-height:1,它应该可以正常工作。
如果这样做不起作用,请尝试以下方法,并让我知道您看到了什么(我不使用Safari或FF)。检查每个浏览器中的 span,工具应该会突出显示一个矩形。看看矩形的顶部是否没有接触线条,或者文本的顶部是否没有接触矩形的顶部。在Chrome中,文本的顶部不接触矩形的顶部,因此解决方案是 line-height:1

1

保持 #btn_signupposition: relative;。同时使用以下CSS将 Sign_up 居中。

#Sign_up {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

它在所有浏览器中都能正常工作。

* {
  margin: 0;
  padding: 0;
}

#btn_signup {
  opacity: 1;
  position: relative;
  width: 185px;
  height: 50px;
  left: 10px;
  top: 10px;
  overflow: visible;
}

#Rectangle_1 {
  opacity: 1;
  fill: transparent;
  stroke: rgb(67, 66, 93);
  stroke-width: 1px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}

.Rectangle_1 {
  position: absolute;
  overflow: visible;
  width: 185px;
  height: 50px;
  left: 0px;
  top: 0px;
}

#Sign_up {
  opacity: 1;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  overflow: visible;
  width: 63px;
  white-space: nowrap;
  text-align: center;
}

#Path_1 {
  opacity: 1;
  fill: rgba(0, 0, 0, 0);
  stroke: rgb(112, 112, 112);
  stroke-width: 0.5px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}

.Path_1 {
  overflow: visible;
  position: absolute;
  top: 16.5px;
  left: 1px;
  width: 183.5px;
  height: 0.5px;
  transform: matrix(1, 0, 0, 1, 0, 0);
}
<div id="A5">
  <div id="btn_signup">
    <svg class="Rectangle_1">
      <rect id="Rectangle_1" rx="4" ry="4" x="0" y="0" width="185" height="50"></rect>
    </svg>
    <div id="Sign_up">
      <span style="font-family:Helvetica;font-style:normal;font-weight:normal;font-size:18px;color:rgba(77,79,92,1);display:inherit;">Sign up</span>
    </div>
    <svg viewBox="12.5 0 183.5 0.5" class="Path_1">
      <path id="Path_1" d="M 12.5 0 L 196 0"></path>
    </svg>
  </div>
</div>


谢谢,我在需要居中时使用这个。如果浏览器能够完美地在各种浏览器中居中,那么它就知道文本内容的边界框。如果他们可以将此应用于允许文本与其垂直像素位置对齐,那么它将提供更多的一致性。如果我或其他人可以将字体绘制到画布上,然后读取第一个非透明垂直像素的像素位置,那么某人就可以手动找出大写字母的高度,然后使用该偏移量在每个浏览器中准确设置垂直位置。 - 1.21 gigawatts

1

在不同的浏览器上,字体呈现效果不同。下面是同一行文字在所有主要浏览器上的截图,叠放在一起以展示问题的大小。(来源:这篇文章

enter image description here

不同字体会产生不同的效果

当我需要将文本垂直对齐像素完美时,我们有一个团队进行了一些研究。我们发现某些字体在垂直方向上比其他字体更好地对齐 - 因此值得测试。

您总是可以使用JS进行黑客攻击

如果是Safari浏览器 - 更改行高(或边距或其他任何属性)即可。

if (navigator.userAgent.toLowerCase().indexOf('safari') > -1) {
  $('.your-element').css({lineHeight: "21px"});
}

在另一条评论中,我建议将文本绘制到画布上,然后读取第一个非透明像素的位置,有人可以手动找到大写字母的高度,然后使用该偏移量在每个浏览器中准确设置垂直位置。但这需要 JavaScript。 - 1.21 gigawatts

0
请在CSS中替换此代码:
#Sign_up {
    opacity: 1;
    box-sizing: border-box;
    overflow: visible;
    white-space: nowrap;
    height: 50px;
    line-height: 50px;
    text-align: center;
    display: block;
}

没有解释其功能的代码质量非常低。 - Asons

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接