使用SVG防止亚像素渲染。

12

我目前正在使用SVG,并遇到了瓶颈。

SVG中有一些线条,它们应该随着缩放一起缩放(以便它们保持平衡:例如,100%宽度10像素--> 10%宽度1像素)

我使用以下代码缩放所有 stroke-widths

var svgPath = this._svgContainer.find('svg [class*="style"]');
for (var i = 0; i < svgPath.length; ++i) {
  var newStrokeWidth = this._oldStrokeWidth[i] * (1 / (width / imgData.w));

  $(svgPath[i]).css(
    'stroke-width', newStrokeWidth
  );
}

其中width是缩放后的新宽度,imgData.w是原始未缩放的宽度。

问题在于,如果我缩放太远,描边宽度会变得太小,导致子像素渲染。据称黑色线条会变成灰色。

我的想法是将值剪切到一定点以防止出现这种情况。但据我所知,我还必须考虑设备像素比,因为不同屏幕(桌面、移动、4K)有所不同。

如果有人能帮我想出解决方法就太好了。


4
可以给一个 jsFiddle 作为示例吗?你所说的缩放是指浏览器缩放,还是通过 JavaScript 控制缩放? - Bacon
1
一个简单的解决方案是使用 vector-effect="non-scaling-stroke"。MDN引用:non-scaling-stroke 是一个预定义的矢量效果关键字,它使得对象的描边宽度不受变换和缩放的影响。 - enxaneta
@Bacon 我通过JavaScript控制缩放。根据缩放级别和指针位置计算描边宽度。我可以提供一个fiddle,但要等到周三,因为我目前在出差中... - breezertwo
也许尝试使用 shape-rendering="crispEdges" - Flash Thunder
2个回答

0
var newStrokeWidth = this._oldStrokeWidth[i] * (1 / (width / imgData.w));
newStrokeWidth = (newStrokeWidth < 1) ? 1 : newStrokeWidth;

newStrokeWidth将始终为1或更大


这是解决问题的错误方式:描边宽度值将为1,但由于视口在(0 0 0 0)和(inf inf inf inf)之间,描边宽度为1在监视器上并不等于1像素。 - breezertwo

0
我们终于找到了解决方案,如果有人遇到同样的问题:
1)由于对this._$svgElement的平移和在代码完全不同的部分计算vpx,该元素处于像素之间(例如x为100.88945px)。这会导致线条变模糊。我通过一个简单的Math.round()来修复这一部分。
this._hammerCanvas.on('panmove', (event: any) => {
        const translate3d = 'translate3d(' + Math.round(this._oldDeltaX + ((vpx === imgData.x) ? 0 : vpx) + event.deltaX) + 'px, ' + Math.round(this._oldDeltaY + ((vpy === imgData.y) ? 0 : vpy) + event.deltaY) + 'px, 0)';
        this._$svgElement.css({
          transform: translate3d
        });
}

2) 为了解决SVG视口和线条粗细之间的问题,我不得不实现一种方法,计算描边宽度等于1个“真实”像素,关于SVG的尺寸。

更新后的代码如下:(这是从服务器加载SVG后的初始代码。在缩放中,上面的旧代码仍然相同)

    const pixelRatio = devicePixelRatio || 1;
    const widthRatio = this._initSVGWidth / svgContainerWidth;
    const heightRatio = this._initSVGHeight / svgContainerHeight;
    this._svgZoomFactor = Math.max(widthRatio, heightRatio);
    const strokeWidth1px = this.computeStrokeWidth1px(widthRatio, heightRatio);

    for (let i = 0; i < svgPaths.length; ++i) {
      this._initalStrokeWidth[i] = parseFloat($(svgPaths[i]).css('stroke-width'));

      const newStrokeWidth = Math.max(strokeWidth1px / pixelRatio, this._svgZoomFactor * this._initalStrokeWidth[i]);

      $(svgPaths[i])[0].setAttribute('style', 'stroke-width:' + newStrokeWidth);
      this._oldStrokeWidth[i] = newStrokeWidth;
    }

和计算机相关:
  protected computeStrokeWidth1px (widthRatio: number, heightRatio: number): number {
    const viewBox = this._$svgElement[0].getAttribute('viewBox').split(' ');
    const viewBoxWidthRatio = parseFloat(viewBox[2]) / this._$svgElement.width();
    const viewBoxHeightRatio = parseFloat(viewBox[3]) / this._$svgElement.height();
    return widthRatio > heightRatio ? viewBoxWidthRatio : viewBoxHeightRatio;
  }

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