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#aperture
SVG#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