I have a HTML webpage with SVG image. I get a problem (excess white line, shown on the picture below) on the webpage when I visit it using iOS Safari or Android Browser. The screenshot resolution is 2x, the saw edge is a SVG image.
I've found out that it happens when the page Y-position of the SVG image is not an integer amount of CSS pixels (px
), i.e. with ½px
. The browser rounds the SVG image position to integer px
when it renders the webpage while doesn't round the other elements positions. That's why the ½px
line appears.
You can reproduce the problem using the snippet below (or this CodePen). You should run the snippet on a device with a high pixel density. You can also reproduce it in desktop Safari if you go to the responsive design mode and pick iPhone or iPad.
.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>
How to prevent ½px
SVG shift on iOS Safari and Android Browser? Is it a bug and I should report it to WebKit developers? Maybe there is a way to make browsers round to px
the other elements on the page?
I can solve this problem without preventing ½px
shift:
.block_content
But I wonder is there a way to prevent ½px
shift because the solutions above are not always possible.
iOS: You just need to add any CSS transform to the SVG element to fix it in Safari. For example .block_edge {-webkit-transform: scale(1); transform: scale(1)}
.
Android: First you need to add a tiny CSS scale transform to the SVG element. When you do it, the <svg>
and the <rect>
elements will be rendered where they must be but the <rect>
background will be repeated at the top and at the bottom:
To fix it you need to extend the pattern to the top and the bottom to prevent background repeating. Then you need to add a filled <rect>
just above the top of the SVG to remove the last blank line at the top. There still will left a hardly visible dark grey line at the top in Android browser.
.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>
I tested it on mobile and desktop Safari 10, Android 4.4 and Chrome 58 on Android.
Conclusion: the fixes are too complicated and not reliable so I advice to make such layout in which half-pixel shift doesn't lead to a blank line.
.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>