如何防止高像素比设备(视网膜)上的半像素 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:
的情况下解决这个问题
- 删除
.block_content
的非整数高度
- 做出这样的布局,其中半像素偏移不会导致while行
但我想知道是否有办法防止 ½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>
我在移动和桌面 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>
您是否考虑过使用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>
我有一个带有 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:
- 删除
.block_content
的非整数高度
- 做出这样的布局,其中半像素偏移不会导致while行
但我想知道是否有办法防止 ½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>
我在移动和桌面 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>
您是否考虑过使用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>