D3.js zoom with nested svg breaks viewport in Inte

2019-02-18 10:21发布

I am using d3.js to dynamically set up a nested svg, i.e. an inner svg nested inside an enclosing svg. A d3.behavior.zoom() listens for zoom events on the outer svg and does the transformations needed.

All works fine except for the Internet Explorer (IE 11) which seems to have an issue with transformations involving inner svgs. Both Firefox and Chrome behave as expected clipping the inner svg to the viewport of the outer svg. In Internet Explorer, however, zooming in correctly applies transformations but seems to ignore the dimensions of the enclosing svg. The contents of the inner svg will eventually be displayed outside of the outer svg and above other body elements. The viewport of the outer svg seems to have no clipping effect on the inner svg.

I have set up a JSFiddle demonstrating the behaviour.

var zoom = d3.behavior.zoom()
    .on("zoom", function () {
        container.attr("transform",
            "translate(" + d3.event.translate + ") " +
            "scale(" + d3.event.scale + ")");
    });

var container = d3.select("body")
                    .append("svg")
                      .attr("id", "svgcontainer")
                      .attr("width", 300)
                      .attr("height", 300)
                      .style("background-color", "#aaaaee")
                      .call(zoom)
                    .append("g");

var svg = container.append("svg")
                     .attr("width", 200)
                     .attr("height", 200)
                     .attr("x", 50)
                     .attr("y", 50);

svg.append("svg:circle")
     .style("fill", "none")
     .style("stroke", "red")
     .style("stroke-width", "2px")
     .attr("cx", 100)
     .attr("cy", 100)
     .attr("r", 50);

Am I missing something? Is there any cross-browser workaround?

1条回答
乱世女痞
2楼-- · 2019-02-18 10:52

I'm sorry this question didn't get enough attention when you first posted it: it's actually a simple fix. Just set the overflow property on the outer SVG to hidden.

So why does your code work as you intend on the other browsers?

It's because they set this property by default. The initial value for overflow in CSS is visible, but the SVG specs require that any element that can take a viewBox attribute has overflow:hidden in the browser's default stylesheet, except for the root SVG element. The other browsers interpret this exception as if it only applied to an <svg> element that is the root of an .svg document. Internet Explorer also applies treats the top-level inline SVG in an HTML document as if was a root (and therefore had overflow: visible).

The following snippet demonstrates the different behaviors. It uses a circle inside a nested SVG inside an inline SVG. The circle is too big for the nested SVG, so if overflow is hidden on the nested SVG (as it is by default in all browsers) the circle will be cropped to a square. The nested SVG is offset, partly outside the outer SVG. If overflow is hidden on the outer SVG, the nested SVG will be cropped to a rectangle; if overflow is visible you'll see the square sticking outside of the frame.

The first SVG uses default overflow on the outer SVG (different for IE) while the others explicitly set overflow: hidden or overflow: visible.

svg {
    border: solid gray;
    height: 100px;
    width: 100px;
    margin: 50px;
}
circle {
    fill: royalBlue;
}
<svg>
    <svg x="-50" y="-50" width="100" height="100" >
        <circle r="100" cx="50" cy="50"/>
    </svg>
</svg>
<svg style="overflow: hidden">
    <svg x="-50" y="-50" width="100" height="100" >
        <circle r="100" cx="50" cy="50"/>
    </svg>
</svg>
<svg style="overflow: visible">
    <svg x="-50" y="-50" width="100" height="100" >
        <circle r="100" cx="50" cy="50"/>
    </svg>
</svg>

The overflow behavior should probably be clarified for SVG 2 or in the SVG integration spec. There is also a discrepancy between Firefox and Blink/Webkit browsers regarding whether or not padding on an inline SVG is considered "overflow" or not.

查看更多
登录 后发表回答