将SVG元素保留在精确像素上

时间:2014-10-24 19:20:00

标签: html css svg

我有一个SVG元素,可以将项目绘制到精确像素。当元素本身放置在DOM中的精确像素上时,它在所有主要浏览器中呈现没有别名。然而,当元素的祖先中存在任何子像素偏移时,它变为别名。

我可以通过将形状渲染设置为清晰边缘或优化速度来防止大多数浏览器中的混叠,但是如果它在错误的方向上旋转,则可能使SVG的内容错位1个像素,因此像素行可能是错过了图像的顶部或侧面。

我可以使用el.getBoundingClientRect()修复问题并调整边距以消除子像素偏移。这工作一次,但如果元素在dom中移动 - 通过滚动,拖动等等,则必须重新应用该方法。这意味着要听DOM,这看起来效率不高。

我想要找到的是CSS规则,或者我可以将SVG包装起来以强制它定位在精确像素上的元素。我想知道是否有一些原生元素 - 例如一个复选框,浏览器可能强制在精确像素上渲染以用于自己的渲染目的。然后我可以将SVG相对于那个......

http://codepen.io/anon/pen/HvCcK(问题最好在Firefox中证明)

HTML:

Crisp on FF and Chrome/Safari:<br/>
<div id="s2" style="width:21px; height:7px; background-color:#FFFFFF;">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" overflow="hidden" viewBox="0 0 50 70" preserveAspectRatio="xMinYMin" style="display: block;"><defs></defs><g style="display: block;">
    <rect x="0" y="0" width="100%" height="100%" fill="red" stroke="none"></rect>
    <path d="M10 10 L 40 10  40 20  20 20  20 30  40 30  40 60  10 60  10 50  30 50  30 40  10 40 Z" fill="white" stroke="none"></path>
    </g></svg>
</div>

<br/>


<div class='offpixel'>
Blurry on Firefox (not on exact pixel):<br/>

<div id="s2" style="width:21px; height:7px; background-color:#FFFFFF;">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" overflow="hidden" viewBox="0 0 50 70" preserveAspectRatio="xMinYMin" style="display: block;"><defs></defs><g style="display: block;">
    <rect x="0" y="0" width="100%" height="100%" fill="red" stroke="none"></rect>
    <path d="M10 10 L 40 10  40 20  20 20  20 30  40 30  40 60  10 60  10 50  30 50  30 40  10 40 Z" fill="white" stroke="none"></path>
    </g></svg>
</div>

<br/>
Crisp on Firefox - but not properly drawn: (not on exact pixel, but uses shape-rendering):<br/>       
<div id="s2" style="width:21px; height:7px; background-color:#FFFFFF;">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" overflow="hidden" viewBox="0 0 50 70" preserveAspectRatio="xMinYMin" style="display: block; shape-rendering: crispedges"><defs></defs><g style="display: block;">
    <rect x="0" y="0" width="100%" height="100%" fill="red" stroke="none"></rect>
    <path d="M10 10 L 40 10  40 20  20 20  20 30  40 30  40 60  10 60  10 50  30 50  30 40  10 40 Z" fill="white" stroke="none"></path>
    </g></svg>
</div>

</div>

CSS:

body{
    font-family:sans-serif;
    font-size:10px;
}
.offpixel {
    padding:3.5px;
}

2 个答案:

答案 0 :(得分:7)

我也搜索了一个解决方案。似乎没有人有答案。无论是在stackoverflow还是在互联网的其余部分。然而,解决方案变得如此简单......

正如您已经猜到的,Firefox并没有将SVG与像素栅格对齐。但是它会对齐通过CSS转换规则转换的元素。因此,只需将svg转换为0像素,它就会与像素栅格对齐。我在代码中附加了一个公开此技术的示例。

&#13;
&#13;
body{
    font-family:sans-serif;
    font-size:10px;
}
.offpixel {
    padding:3.5px;
}
.crisp {
    transform: translate(0, 0);
}
&#13;
<div class="crisp">
<!-- ^^ Reset. Just in case this snippet adds some unwanted subpixel offset -->
Crisp on FF and Chrome/Safari:<br/>
<div id="s2" style="width:21px; height:7px; background-color:#FFFFFF;">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" overflow="hidden" viewBox="0 0 50 70" preserveAspectRatio="xMinYMin" style="display: block;"><defs></defs><g style="display: block;">
    <rect x="0" y="0" width="100%" height="100%" fill="red" stroke="none"></rect>
    <path d="M10 10 L 40 10  40 20  20 20  20 30  40 30  40 60  10 60  10 50  30 50  30 40  10 40 Z" fill="white" stroke="none"></path>
    </g></svg>
</div>

<br/>


<div class='offpixel'>
Blurry on Firefox (not on exact pixel):<br/>

<div id="s2" style="width:21px; height:7px; background-color:#FFFFFF;">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" overflow="hidden" viewBox="0 0 50 70" preserveAspectRatio="xMinYMin" style="display: block;"><defs></defs><g style="display: block;">
    <rect x="0" y="0" width="100%" height="100%" fill="red" stroke="none"></rect>
    <path d="M10 10 L 40 10  40 20  20 20  20 30  40 30  40 60  10 60  10 50  30 50  30 40  10 40 Z" fill="white" stroke="none"></path>
    </g></svg>
</div>

<br/>
Crisp on Firefox - but not properly drawn: (not on exact pixel, but uses shape-rendering):<br/>       
<div id="s2" style="width:21px; height:7px; background-color:#FFFFFF;">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" overflow="hidden" viewBox="0 0 50 70" preserveAspectRatio="xMinYMin" style="display: block; shape-rendering: crispedges"><defs></defs><g style="display: block;">
    <rect x="0" y="0" width="100%" height="100%" fill="red" stroke="none"></rect>
    <path d="M10 10 L 40 10  40 20  20 20  20 30  40 30  40 60  10 60  10 50  30 50  30 40  10 40 Z" fill="white" stroke="none"></path>
    </g></svg>
</div>
<br/>
Crisp on Firefox and correctly drawn (uses transform snapping):<br/>

<div id="s2" style="width:21px; height:7px; background-color:#FFFFFF;">
    <svg class="crisp" xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" overflow="hidden" viewBox="0 0 50 70" preserveAspectRatio="xMinYMin" style="display: block;"><defs></defs><g style="display: block;">
    <rect x="0" y="0" width="100%" height="100%" fill="red" stroke="none"></rect>
    <path d="M10 10 L 40 10  40 20  20 20  20 30  40 30  40 60  10 60  10 50  30 50  30 40  10 40 Z" fill="white" stroke="none"></path>
    </g></svg>
</div>

</div>
</div>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

您遇到了设计问题,而不是标记/样式问题。

将SVG代码像素完美呈现的责任应该是SVG代码,页面代码和浏览器行为的共同责任。

所以我要说,保持SVG代码不变,因为它是完美的像素。

页面和标记设计应该是像素完美的,因此在更高级别的设计上没有3.5px值。

然后你仍然有滚动/缩放问题,我认为getBoundingClientRect方法是正确的。但如果您害怕表现,可能会将其附加到计时器事件中。