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

时间:2017-05-24 23:56:13

标签: html css svg retina

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

Expectation vs. result

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

Explanation

您可以使用下面的代码段(或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>

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

我可以在不阻止½px移位的情况下解决此问题:

  • 删除.block_content
  • 的非整数高度
  • 进行这样的布局,其中半像素移位不会导致行

但我想知道有没有办法防止½px转变,因为上述解决方案并非总是可行。

3 个答案:

答案 0 :(得分:5)

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

Android:首先,您需要为SVG元素添加一个微小的CSS比例变换。执行此操作后,<svg><rect>元素将呈现在必须的位置,但<rect>背景将在顶部和底部重复显示:

enter image description here

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

&#13;
&#13;
.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);
}
&#13;
<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>
&#13;
&#13;
&#13;

The snippet on CodePen

我在Android上的移动和桌面Safari 10,Android 4.4和Chrome 58上进行了测试。

结论:修复过于复杂且不可靠,所以我建议制作半像素移位不会导致空白行的布局。

&#13;
&#13;
.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;
}
&#13;
<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>
&#13;
&#13;
&#13;

The snippet on CodePen

答案 1 :(得分:1)

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

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

&#13;
&#13;
.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;
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 2 :(得分:1)

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

修改

在snipped下面使用单个\/重复并在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>