dagre-d3
dagre-d3 copied to clipboard
Chrome 53 node rect +(devicePixelRatio*100)% padding
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: "Helvetica Neue", 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):
Chrome 53.0 (latest as of writing this):
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 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).
This is still an issue as of Chrome 67, but @MajorBreakfast's fix works quite well.
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 I referenced this issue by mistake in a PR, sorry, it's completely unrelated.
@deanp70 what’s wrong with my workaround?
@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?
@oxygen, could you explain the following line?
innerWidth / outerWidth < 1.4
Why 1.4?
@deanp70 Read the comment next to it.
@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?
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