Jump to content
Search Community

scaling a path with non-scaling-stroke causes Safari browser to leave tracers

swampthang test
Moderator Tag

Recommended Posts

I wonder of anyone knows a workaround for this. I need to be able to set an absolute size for a stroke width - not having it grow or shrink with the scaling of the path. The codepen works beautifully in all browsers except Safari. Attached is a screenshot of what happens. All I can do at this point is add a window.navigator.userAgent check for Safari and change the vector-effect value to be 'non-scaling-size' which does nothing really in this scenario.

Any thoughts?

Screen Shot 2021-05-21 at 10.39.22 AM.png

See the Pen ZEeBWVr by swampthang (@swampthang) on CodePen

  • Like 1
Link to comment
Share on other sites

Oh, I've been seeing that everywhere lately, not just with SVGs, and not just in Safari. If you resize the screen, they go away. That causes the browser to redraw everything. Obviously resizing your screen isn't the solution, but you have find a way to force the browser to redraw.

 

There are ways, I just can't remember the best way to do it right now.

 

  • Like 1
Link to comment
Share on other sites

Safari seems to clip the bounding box incorrectly, and when rendering it only cares about what's inside that bounding box. Strokes put half of their width on each side, thus I think Safari is only caring about the actual path position and forgetting to factor in that overflow of the stroke outside of the path when it calculates the area it should care about. Clearly a Safari bug, but one way you could maybe trick it is to put a transparent <rect> in your <g> that expands a bit beyond where the stroke is in order to force Safari to care about rendering that area too. Here's a fork with a helper function that does exactly that, but it's not intended to solve every case - it's pretty much for <g> elements in this case although perhaps those are the only things that the Safari bug affects: 

See the Pen rNyyadB?editors=0010 by GreenSock (@GreenSock) on CodePen

 

// add a transparent <rect> inside the <g> that expands the bounding box.
function fixRendering(targets, amount) {
  amount = amount || 10;
  gsap.utils.toArray(targets).forEach(el => {
    if (el.getBBox && el.appendChild) {
      const bbox = el.getBBox(),
            rect = document.createElementNS((el.ownerSVGElement && el.ownerSVGElement.getAttribute("xmlns")) || "http://www.w3.org/2000/svg", "rect");
      gsap.set(rect, {attr:{x: bbox.x - amount, y: bbox.y - amount, width: bbox.width + amount * 2, height: bbox.height + amount * 2, fill: "transparent"}});
      el.appendChild(rect);
    }
  });
}

 

Does that help? Seemed to work for me in Safari. 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

27 minutes ago, GreenSock said:

Safari seems to clip the bounding box incorrectly, and when rendering it only cares about what's inside that bounding box. Strokes put half of their width on each side, thus I think Safari is only caring about the actual path position and forgetting to factor in that overflow of the stroke outside of the path when it calculates the area it should care about. Clearly a Safari bug, but one way you could maybe trick it is to put a transparent <rect> in your <g> that expands a bit beyond where the stroke is in order to force Safari to care about rendering that area too. Here's a fork with a helper function that does exactly that, but it's not intended to solve every case - it's pretty much for <g> elements in this case although perhaps those are the only things that the Safari bug affects: 

 

 

 


// add a transparent <rect> inside the <g> that expands the bounding box.
function fixRendering(targets, amount) {
  amount = amount || 10;
  gsap.utils.toArray(targets).forEach(el => {
    if (el.getBBox && el.appendChild) {
      const bbox = el.getBBox(),
            rect = document.createElementNS((el.ownerSVGElement && el.ownerSVGElement.getAttribute("xmlns")) || "http://www.w3.org/2000/svg", "rect");
      gsap.set(rect, {attr:{x: bbox.x - amount, y: bbox.y - amount, width: bbox.width + amount * 2, height: bbox.height + amount * 2, fill: "transparent"}});
      el.appendChild(rect);
    }
  });
}

 

Does that help? Seemed to work for me in Safari. 

Yes! Thank you! Loving the other input too. I didn't realize this was an issue in Chrome on Windows since I'm on Mac myself. 

Link to comment
Share on other sites

12 minutes ago, GreenSock said:

I'm curious - do you notice any difference if you do either of the following?:

  • set force3D: false
  • apply will-change: transform

 

Yep, those both seem to fix it, although the will-change option is probably the worst because it doesn't repaint it, so it gets pixelated when scaled up.

 

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

15 hours ago, GreenSock said:

Safari seems to clip the bounding box incorrectly, and when rendering it only cares about what's inside that bounding box. Strokes put half of their width on each side, thus I think Safari is only caring about the actual path position and forgetting to factor in that overflow of the stroke outside of the path when it calculates the area it should care about. Clearly a Safari bug, but one way you could maybe trick it is to put a transparent <rect> in your <g> that expands a bit beyond where the stroke is in order to force Safari to care about rendering that area too. Here's a fork with a helper function that does exactly that, but it's not intended to solve every case - it's pretty much for <g> elements in this case although perhaps those are the only things that the Safari bug affects: 

 

 

 


// add a transparent <rect> inside the <g> that expands the bounding box.
function fixRendering(targets, amount) {
  amount = amount || 10;
  gsap.utils.toArray(targets).forEach(el => {
    if (el.getBBox && el.appendChild) {
      const bbox = el.getBBox(),
            rect = document.createElementNS((el.ownerSVGElement && el.ownerSVGElement.getAttribute("xmlns")) || "http://www.w3.org/2000/svg", "rect");
      gsap.set(rect, {attr:{x: bbox.x - amount, y: bbox.y - amount, width: bbox.width + amount * 2, height: bbox.height + amount * 2, fill: "transparent"}});
      el.appendChild(rect);
    }
  });
}

 

Does that help? Seemed to work for me in Safari. 


This is such a clever solution. Super privileged to be able to learn stuff like this from you Jack. 

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...