Reimplementation of Focus + Context visualization, using SVG's viewBox attribute for the zoom/pan instead.
This approach uses SVG's viewBox to achieve the following effects:
focus and one in context, with the one in focus getting re-rendered dynamically on every view change (zoom/pan), and the one in context only getting rendered once.focus is three SVGs deep.SVG#aperture is at the same position and size of the clipping rectangle
<g> element named focus is replaced by an <svg> element named 'focus'
SVG#focus is nested within SVG#apertureSVG#focus is at the same position and size of <g#focus>'s bounding box<g> element named context is replaced by an <svg> element named 'context'
SVG#context is at the same position and size of the <g#context>'s bounding boxThe layout is something like this:
<svg width=960 height=500>
<SVG#aperture width=900 height=370 viewBox="0 0 900 370" preserveAspectRatio="none">
<SVG#focus width=900 height=370><svg#context width=900 height=40 viewBox="0 0 900 370">
<use xlink:href="#focus">The topmost SVG is the container for everything else. Its width and height attributes do two things:
The aperture SVG sets a width and height for its physical dimensions within the topmost SVG. We set its viewBox attribute to define its internal (or user) coordinate system. We set its preserveAspectRatio attribute to none so that its contents will stretch to fit the entire viewport.
The focus SVG sets a width and height identical to its parent, thus it fully covers its parent aperture. This sets both its internal and external dimensions. Since the area chart doesn't change, the focus SVG will always show the full area graph. We will use SVG#aperture's viewBox attribute to display the requested portions of the focus SVG.
Finally the context SVG is where the width/height and viewBox will diverge: the width is the same, but the height is the height of the actual SVG#context (40 in this example). The internal coordinates are set to the same as the the focus SVG. In order to show the contents of SVG#focus, we use the <use> call which shows an internal clone of SVG#focus. Thus, SVG#focus is simply duplicated and squished to fit the dimensions of SVG#context. Since context's internal coordinates are the same as focus's dimensions, it will fully fit. Note that context will also show any animations/transitions/changes that you put on focus due to being a clone.
The brush on SVG#context will now be the same dimensions as SVG#focus, since both coordinate systems are identical. This means that whatever the size of this rectangle is after brushing/zooming will map directly to the new viewBox value in aperture to implement the actual zoom/pan effect on focus. Basically aperture is like a window/lens on top of focus and we can adjust how close in or how far out we want to look through that window/lens to see which bits of focus below.
The axes are contained within the top-level SVG because they need to exist outside of all the zooming/panning happening, and they get updated in the same way as before.
The brush and zoom functions are updated to adjust the aperture's viewBox as well as the focus x-axis.
https://d3js.org/d3.v5.min.js