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

14

我有一个包含 SVG 图像的 HTML 网页。当我使用 iOS Safari 或 Android 浏览器访问它时,页面上出现了问题(如下图所示的白线过多)。屏幕截图分辨率为 2x,这条锯齿边缘是一张 SVG 图像。

Expectation vs. result

我发现问题出现在 SVG 图像的页面 Y 轴位置不是 CSS 像素(px)的整数倍时,即当位置是 ½px 时。当浏览器渲染网页时,它会将 SVG 图像位置四舍五入为整数 px,而不会将其他元素位置四舍五入。这就是为什么会出现 ½px 的线。

Explanation

你可以使用下面的代码片段(或这个 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偏移?这是个bug吗?需要向WebKit开发者报告吗?也许有一种方法可以使浏览器将页面上的其他元素舍入为px
我可以解决此问题,而无需防止½px的偏移:
- 删除.block_content的非整数高度 - 进行这样的布局,其中半像素偏移不会导致白线
但我想知道是否有一种方法可以防止½px的偏移,因为上述解决方案并非总是可行。

1
当然,你在移动设备上无法单击“运行代码段”按钮,因为该按钮被隐藏了。 - inorganik
@inorganik 我已经在CodePen上为移动用户添加了代码片段的链接。 - Finesse
谢谢。在Codepen中它没有以正确的视口宽度呈现,但它看起来非常清晰。 - inorganik
@inorganik 在CodePen移动界面中有一个屏幕分辨率切换器。您可以选择1x比例使其更清晰。 - Finesse
或者使用嵌入式SVG和::after CSS规则添加Zigzag。 - Ruskin
显示剩余2条评论
3个回答

11

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

Android: 首先,您需要向SVG元素添加一个微小的CSS缩放变换。当您这样做时,<svg><rect> 元素将被渲染到它们必须存在的位置,但是 <rect> 背景将在顶部和底部重复:

enter image description here

要解决此问题,您需要将图案扩展到顶部和底部,以防止背景重复。然后,您需要在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>

CodePen上的片段

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

结论:修复过于复杂且不可靠,因此我建议采用这样的布局,在其中半像素偏移不会导致空格。

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

这个CodePen的代码片段


1
我在Windows上的Chrome中测试,使用Galaxy S5时看到一条线,在尝试Nexus时看到一条线在牙齿下方。我还注意到在iPhone 6上有一条线和一条薄灰线在牙齿下方。 - gwally
@gwally 是的,我能看到。但这是另一个问题。<svg><rect> 被渲染在它们应该呈现的位置,但是 <rect> 的背景向下移动了 1 像素并在顶部重复(就像 CSS 中的 background-repeat: repeat; background-position: 0 1px;)或者反之亦然。 - Finesse
1
哦天啊,我已经为此苦苦挣扎了几个小时了。谢谢!(在我的情况下帮了我) - Shulyk Volodymyr

1

你是否考虑使用CSS来插入边框?

编辑

以下代码段使用单个\/重复,在Android(Chrome)和iOS上都可以工作。我可以通过在iOS上放大来触发微弱的发际线。这可以通过在三角形上方添加一个块并将::after与其父元素重叠来修复。

Codepen版本,适用于移动设备测试

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


你可以大幅度简化这段代码:将SVG变成一个单一的 \/ 三角形,并在CSS中设置大小和repeat-x,例如 <path d='m1 1L2 0H0z'/> - Ruskin
Chris Coyier在他的视频中做了类似的事情,视频链接为https://css-tricks.com/lodge/svg/06-using-svg-svg-background-image/。 - Ruskin
当SVG作为背景使用时,它能够工作,但以1x分辨率渲染,导致图像的锯齿较为明显。此外,这个答案中描述的Android问题尚未解决。 - Finesse
修改了您上面提出的问题。 - Ruskin

1
.block_content添加outline: 1px solid #000;。这将填补iPhone 6上两个SVG图形之间的间隙。我意识到这会创建一个间距问题,但它可以解决间隙问题。
解决这个问题的方法是创建一个@media查询,在影响iPhone 4-6的尺寸下只向.block_content添加outline

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


谢谢。但是这是一个布局,半像素移位不会导致白线,我在问题中提到了它。我想知道是否有一种方法可以修复半像素移位,而不是解决它。 - Finesse
更好的方法是通过在顶部添加黑线并使用 margin 将 <svg> 上移来扩展 SVG 图像。 - Finesse
IOS在电子邮件中也存在类似的限制,即在两个相互堆叠的表格之间添加了一个白色线条。即使您添加了有颜色背景的表格,表格之间的空间仍然显示为白色线条。除非苹果修复这个限制,否则您将会看到一个白色线条。这会影响iPhone 4-6。 即使您添加黑色线条,最可能出现的情况是顶部图形、线条和底部图形之间会出现一个白色线条分隔。 - gwally
另一个解决方法是将图形作为一个对象创建,而不是两个。这样就没有白线了。 - gwally

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