如何防止高像素比设备(视网膜)上的半像素 SVG 偏移?

How to prevent half-pixel SVG shift on high pixel ratio devices (Retina)?

我有一个带有 SVG 图像的 HTML 网页。当我使用 iOS Safari 或 Android 浏览器访问网页时,出现问题(白线过多,如下图所示)。屏幕截图分辨率为2x,锯边为SVG图像。

我发现当 SVG 图像的页面 Y 位置不是整数 CSS 像素 (px) 时,即 ½px.浏览器在呈现网页时将 SVG 图像位置舍入为整数 px 而不会舍入其他元素位置。这就是 ½px 行出现的原因。

您可以使用下面的代码片段(或 this CodePen)重现该问题。您应该 运行 具有高像素密度的设备上的代码段。如果您进入响应式设计模式并选择 iPhone 或 iPad.

,您也可以在桌面 Safari 中重现它

.common-bg {
  background: #222;
  fill: #222;
}
.block {
  max-width: 300px;
  margin: 20px auto;
}
.block_content {
  height: 50.5px;
}
.block_edge {
  display: block;
}
<div class="block">
  <div class="block_content common-bg"></div>
  <svg
    class="block_edge"
    width="100%"
    height="10"
    xmlns="http://www.w3.org/2000/svg"
    version="1.1"
    xmlns:xlink="http://www.w3.org/1999/xlink"
  >
    <defs>
      <pattern id="sawPattern" x="50%" width="20" height="10" patternUnits="userSpaceOnUse">
        <path d="M 0 0 L 10 10 L 20 0 Z" class="common-bg"/>
      </pattern>
    </defs>
    <rect x="0" y="0" width="100%" height="10" fill="url(#sawPattern)"/>
  </svg>
</div>

如何防止 ½px SVG 在 iOS Safari 和 Android 浏览器上偏移?这是一个错误吗?我应该向 WebKit 开发人员报告它吗?也许有一种方法可以让浏览器转向 px 页面上的其他元素?

我可以在不阻止½px shift:

的情况下解决这个问题

但我想知道是否有办法防止 ½px 偏移,因为上述解决方案并不总是可行的。

outline: 1px solid #000; 添加到 .block_content。这将填补 iPhone 6 上两个 svg 图形之间的空隙。我意识到这会产生间距问题,但它修复了空隙。

该问题的解决方案是创建一个@media 查询,您只在 outline.block_content 中添加影响 iPhone 4-6 的大小。

.block {
  max-width: 300px;
  margin: 20px auto;
}
.block_content {
  background: #000;
  font-size: 10px;
  height: 50.5px;
  outline: 1px #f00 solid;
}
.block_edge {
  display: block;
}
.block_edge path {
  fill: #000;
}
<div class="block">
  <div class="block_content"></div>
  <svg
    class="block_edge"
    width="100%"
    height="10"
    xmlns="http://www.w3.org/2000/svg"
    version="1.1"
    xmlns:xlink="http://www.w3.org/1999/xlink"
  >
    <defs>
      <pattern id="sawPattern" x="50%" width="20" height="10" patternUnits="userSpaceOnUse">
        <path d="M 0 0 L 10 10 L 20 0 Z"/>
      </pattern>
    </defs>
    <rect id="Line" x="0" y="0" width="100%" height="10" fill="url(#sawPattern)"/>
  </svg>
</div>

iOS: 您只需将任何 CSS 转换添加到 SVG 元素即可在 Safari 中修复它。例如 .block_edge {-webkit-transform: scale(1); transform: scale(1)}.

Android: 首先,您需要向 SVG 元素添加一个微小的 CSS 比例变换。当你这样做时,<svg><rect> 元素将呈现在它们必须出现的位置,但 <rect> 背景将在顶部和底部重复:

要修复它,您需要将图案扩展到顶部和底部以防止背景重复。然后,您需要在 SVG 顶部的正上方添加一个填充的 <rect> 以删除顶部的最后一个空白行。在 Android 浏览器中,顶部仍然会留下一条几乎看不见的深灰色线。

.common-bg {
  background: #222;
  fill: #222;
}
.block {
  max-width: 300px;
  margin: 20px auto;
}
.block_content {
  height: 50.5px;
}
.block_edge {
  display: block;
  
  /* Fix. No more than 5 zeros. */
  -webkit-transform: scale(1.000001);
  transform: scale(1.000001);
}
<div class="block">
  <div class="block_content common-bg"></div>
  <svg
    class="block_edge"
    width="100%"
    height="10"
    xmlns="http://www.w3.org/2000/svg"
    version="1.1"
    xmlns:xlink="http://www.w3.org/1999/xlink"
  >
    <defs>
      <pattern id="sawPattern" x="50%" y="-1" width="20" height="12" patternUnits="userSpaceOnUse">
        <path d="M 0 0 L 0 1 L 10 11 L 20 1 L 20 0 Z" class="common-bg"/>
      </pattern>
    </defs>
    <rect x="0" y="-1" width="100%" height="1" common-bg="common-bg"/>
    <rect x="0" y="0" width="100%" height="10" fill="url(#sawPattern)"/>
  </svg>
</div>

The snippet on CodePen

我在移动和桌面 Safari 10、Android 4.4 和 Chrome 58 Android 上测试了它。

结论:修正太复杂且不可靠所以我建议做这样的布局,其中半像素偏移不会导致空行。

.common-bg {
  background: #222;
  fill: #222;
}
.block {
  max-width: 300px;
  margin: 20px auto;
}
.block_content {
  height: 50.5px;
}
.block_edge {
  display: block;
  
  /* Overflow for unexpected translateY */
  margin-top: -1px;
}
<div class="block">
  <div class="block_content common-bg"></div>
  <svg
    class="block_edge"
    width="100%"
    height="12"
    xmlns="http://www.w3.org/2000/svg"
    version="1.1"
    xmlns:xlink="http://www.w3.org/1999/xlink"
  >
    <defs>
      <!-- The teeth pattern is extended to the top -->
      <pattern id="sawPattern" x="50%" width="20" height="12" patternUnits="userSpaceOnUse">
        <path d="M 0 0 L 0 1 L 10 11 L 20 1 L 20 0 Z" class="common-bg"/>
      </pattern>
    </defs>
    <rect x="0" y="0" width="100%" height="11" fill="url(#sawPattern)"/>
  </svg>
</div>

The snippet on CodePen

您是否考虑过使用css来注入边框?

编辑

下面的片段使用单个 \/ 重复并适用于 android (chrome) 和 iOs。我可以通过放大 iOs 来触发微弱的发际线。这可以通过在三角形上方添加一个块并将 ::after 与其父元素重叠来解决。

Codepen version for testing on mobile

.block {
  max-width: 300px;
  margin: 20px auto;
}

.block_content {
  background: #000;
  font-size: 10px;
  height: 50.5px;
  position: relative;
}

.block_content::after {
  content: '';
  position: absolute;
  height: 1em;
  width: 100%;
  top: 100%;
  background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg width='2' height='1' xmlns='http://www.w3.org/2000/svg' %3e%3cpath d='m1 1L2 0H0z'/%3e%3c/svg%3e");
  /* bg generated from https://codepen.io/elliz/full/ygvgay */ 
  background-size: 2em 1em;
  background-repeat: repeat-x;
}
<div class="block">
  <div class="block_content"></div>
</div>