dagre-d3 icon indicating copy to clipboard operation
dagre-d3 copied to clipboard

Chrome 53 node rect +(devicePixelRatio*100)% padding

Open oxygen opened this issue 7 years ago • 11 comments

Starting with Chrome 53, a huge padding space shows up for HTML inside SVG.

On Windows 10, set DPI to 300% and log off (the higher the DPI, the more weird padding). Then log back in.

Load this is an older Chrome or in Firefox: http://cpettitt.github.io/project/dagre-d3/latest/demo/interactive-demo.html

Then load it in Chrome 53.0 (latest as of writing this). See the bigger padding space from the HTML edges to the rect edges.

You will see that where it says "A Big HTML Source" the width and height of the rect inside the first g of a node (class="node") are calculated to be about 50% larger relative to the HTML's width and height.

<g class="node" transform="translate(145.23046875,57.75)" style="opacity: 1;">
    <rect rx="5" ry="5" x="-125.23046875" y="-37.75" width="250.4609375" height="75.5"></rect>
<g class="label" transform="translate(0,0)"><g transform="translate(-115.23046875,-27.75)"><foreignObject width="230.4609375" height="55.5"><div xmlns="http://www.w3.org/1999/xhtml" style="font-style: normal; font-variant: normal; font-weight: 300; font-stretch: normal; font-size: 14px; line-height: normal; font-family: &quot;Helvetica Neue&quot;, Helvetica; display: inline-block; white-space: nowrap;">A <span style="font-size:32px">Big</span> <span style="color:red;">HTML</span> Source!</div></foreignObject></g></g></g>

The bigger the HTML, the bigger the "padding".

Here's a screenshot from a private app. Firefox (I don't have an older Chrome anymore): image

Chrome 53.0 (latest as of writing this): image

oxygen avatar Sep 07 '16 16:09 oxygen

Found a workraound.

It appears that Chrome amplifies some margin/padding when zooming. When changing the zoom it fails to correctly scale back the padding or margin (not CSS propertie values themselves, but the screen space they took when zoomed).

Ever since the above mentioned Chrome version, I think the engine starts zoomed already and then goes back to 100% (default) somehow incompletely.

Workaround the issue:

// var graph = new dagreD3.graphlib.Graph({compound: true});
// var dagreRenderer = new dagreD3.render();

function dagreRenderWrap()
{
	var bWebKitRetinaZoomMarginBugWorkaroundEnable = 
		window.devicePixelRatio > 1 
		&& navigator.userAgent.search(/WebKit/) > -1
		&& innerWidth / outerWidth < 1.4 // prevent fonts from overflowing in case of browser zoom
	;

	if(bWebKitRetinaZoomMarginBugWorkaroundEnable)
	{
		// Zoom out, so the orphaned alien padding is as small as possible.
		document.getElementById("svg-canvas").style.zoom = 1/window.devicePixelRatio;
	}

	dagreRenderer(d3.select("svg g"), graph);

	if(bWebKitRetinaZoomMarginBugWorkaroundEnable)
	{
		document.getElementById("svg-canvas").style.zoom = 1;
	}
}

//<svg id="svg-canvas"> somewhere on page.

I think this could be included as some sort of patch? We'll see.

oxygen avatar Feb 21 '17 12:02 oxygen

@oxygen Thanks a lot for sharing this!

Here's the code I'm now using:

let isOpera = (!!window.opr && !!opr.addons) || !!window.opera ||
      navigator.userAgent.indexOf(' OPR/') >= 0
let isChrome = !!window.chrome && !!window.chrome.webstore
let isBlink = (isChrome || isOpera) && !!window.CSS
let useZoomWorkaround = window.devicePixelRatio > 1 && isBlink

function zoomWorkaroundBeforeDagreRender (svg) {
  if(useZoomWorkaroundBeforeRender) {
    svg.style.zoom = 1 / window.devicePixelRatio
  }
}

function zoomWorkaroundAfterDagreRender (svg) {
  if(useZoomWorkaroundBeforeRender) {
    svg.style.zoom = 1
  }
}
var svg = document.getElementById('my-svg-element')
zoomWorkaroundBeforeDagreRender(svg)
dagreRender(d3.select(svg).select('g'), graph)
zoomWorkaroundAfterDagreRender(svg)

Note: It's also possible to create a function that patches the dagreRender() function by using Object.setPrototypeOf() (to make methods like dagreRender.arrows() work), but I ended up using this solution as it's much easier to understand.

Edit: Had to add some more complicated browser detection, because this is only broken in Blink (Chrome 60, didn't look at Opera, but I assume it's broken as well), it's fixed in Webkit (I checked on iOS 11 beta. I've no idea how long this has been fixed, but it's fixed now - that's what counts).

MajorBreakfast avatar Sep 01 '17 15:09 MajorBreakfast

This is still an issue as of Chrome 67, but @MajorBreakfast's fix works quite well.

agg23 avatar Aug 01 '18 23:08 agg23

The workaround doesn't work for me. Before I define the svg.style.zoom within the functions (suggested by @MajorBreakfast) it doesn't exist (printing it out in the console returns undefined). It does not seem to have any effect on the outcome.

Does anyone have a full working example of a solved case @agg23 @cpettitt @futpib? that would be really helpful

deanp70 avatar Apr 07 '19 14:04 deanp70

@deanp70 I referenced this issue by mistake in a PR, sorry, it's completely unrelated.

futpib avatar Apr 07 '19 14:04 futpib

@deanp70 what’s wrong with my workaround?

oxygen avatar Apr 07 '19 15:04 oxygen

@deanp70 what’s wrong with my workaround?

I tried implementing it as well. the problem is that my svg object has no attirbute style attribute named zoom, so changing it adds the attribute but doesn't affect the rendering of the graph.

If you have any ideas why that might happen that would be really helpful :)

Edit: @oxygen I retried your solution and it works...I'm gonna spend a while longer understanding why it didn't earlier, BUT A HUGE THANKS!

Edit 2: @oxygen I now noticed that even though it fixes the problem with chrome on windows computers, it causes a different problem in Macs that didn't exist before. Do you have an idea on how to fix it?

deanp70 avatar Apr 07 '19 15:04 deanp70

@oxygen, could you explain the following line? innerWidth / outerWidth < 1.4 Why 1.4?

deanp70 avatar Apr 08 '19 07:04 deanp70

@deanp70 Read the comment next to it.

oxygen avatar Apr 09 '19 16:04 oxygen

@deanp70 Read the comment next to it.

@oxygen I understand the reason for the condition, I'm asking about the magic number. Why not 1.2? or maybe 3?

deanp70 avatar Apr 09 '19 18:04 deanp70

I have this issue on mobile devices only in current Chrome-Mobile 79. Firefox-Mobile working fine. Maybe this one: https://bugs.chromium.org/p/chromium/issues/detail?id=738022&q=foreign-object

Daijobou avatar Jan 28 '20 07:01 Daijobou