13

I'm working with SVGs currently and came to a dead end.

The SVG has lines, which should scale together with zooming (so that they stay in balance: 100% width 10px --> 10% width 1px for example)

i scale all stroke-widths with this code:

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
  );
}

Where width is the new width after zoom and imgData.w is the original unscaled width.

The problem with this is, if i zoom in to far. The stroke with becomes to small and leads to sub-pixel rendering. And supposedly black lines get grey-ish.

My Idea was to clip the value at a certain point to prevent it. But as far as I know, I have to consider the Device Pixel ratio too, because of different screens (desktop, mobile, 4K)

Would be nice If someone can help me with an idea to fix my problem

Towkir
  • 3,889
  • 2
  • 22
  • 41
breezertwo
  • 585
  • 7
  • 26
  • 4
    could you provide a fiddle as example? by zooming do you mean, browser zoom, or do you control zomming via javascript? – Bacon Jun 03 '19 at 07:46
  • 1
    One _easy_ solution to your problem would be using `vector-effect="non-scaling-stroke"` **MDN quote:** The value `non-scaling-stroke` is a keyword for a predefined vector effect that causes **an object's stroke-width to be unaffected by transformations and zooming**. – enxaneta Jun 04 '19 at 15:13
  • @Bacon i controll the zooming via javascript. based on the zoom level and the position of the pointer the stroke-width is calculated. I can provide a fiddle but not until wednesday cause i#m currently on a business trip... – breezertwo Jun 04 '19 at 15:25
  • maybe try `shape-rendering="crispEdges"` ? – Flash Thunder Jul 31 '19 at 13:09

2 Answers2

1

We finally found a solution for this, in case anyone has the same problems:

1) Because of the panning of this._$svgElement and the calculation of vpx in a completely different section of the code the element is 'between' pixels. ( 100.88945px for x for example). This causes lines to blur. I fixed this part with a simple 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) To fix the problem between the SVG viewport and the line strength, I had to implement a method to calculate the strokewidth equal to 1 'real' pixel regarding the svgs dimension.

the updated code looks like this: (This is the inital code, after the SVG was loaded from the server. Inside the zooming, the old code from above is still the same)

    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;
    }

and the compute:

  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;
  }
breezertwo
  • 585
  • 7
  • 26
0
var newStrokeWidth = this._oldStrokeWidth[i] * (1 / (width / imgData.w));
newStrokeWidth = (newStrokeWidth < 1) ? 1 : newStrokeWidth;

newStrokeWidth will always be 1 or greater

  • This is the wrong way to solve it: the stroke width value would be 1 but due to the viewport beeing anything between (0 0 0 0) and (inf inf inf inf) a stroke-width of one does not equal 1 px on the monitor. – breezertwo Aug 06 '19 at 14:02