This is the 16th day of my participation in the First Challenge 2022


See the Data Visualization column for a series of articles


Reference:

  • Learn D3: Interaction
  • Zooming (d3-zoom)
  • D3-zoom (Unofficial Chinese Translation)

This article focuses on the Zooming module


Panning and Zooming are a common interaction where clicking and dragging can translate an image (modifying the CSS Translate style properties of the image accordingly), By rolling the mouse wheel/(on touchscreen devices) two-finger pinching, the image can be scaled (modifying the CSS Scale style properties of the graph accordingly), allowing the user to focus on exploring the parts of interest in a limited view.

The D3-ZOOM module provides a simple and flexible way to pan and zoom SVG, HTML or Canvas. In addition to manipulating selection sets, it also supports scaling and panning scales and coordinate axes. The module also provides methods for programmatically panning and zooming, as well as setting up smooth transition animations.

Create a zoomer

Create a zoomer (hereafter called zoom) using the method d3.zoom()

It is both a method that takes a selection set as parameter zoom(Selection), adds corresponding zoom event listeners to the elements in the selection set (typically the container

elements that contain the data), and sets them the initial value of the transform

// The zoomer creation function is usually called through the selection.call() method
// The selection selection set is passed as an argument to the zoomer creation function
selection.call(d3.zoom().on("zoom", zoomed));
Copy the code

In the above example, the zoomer adds a zoom event listener (the zoom event type defined in D3) for the elements in the selection set (container < G >), and sets the corresponding handler zoomed (generally, transform-related style attributes are modified in the callback function to achieve the zooming and panning of elements in a specific view. This is usually applied to the

element in the container, so that all the data in the container is translated with the scale.

💡 If you want to remove listeners for zooming events, you can set the corresponding event callback function to NULL

selection.on(".zoom".null);

// It is also possible to remove listeners only for specific zoom events, such as wheel zoom
selection.on("wheel.zoom".null);
Copy the code

It is also an object that provides a variety of methods related to scaling, such as setting up native click-and-drag events triggered by listener users to trigger scaling; You can also call the corresponding method to perform scaling programmatically; The scaling behavior can also be constrained by corresponding methods.

Constraint scale

  • Zoom. Constrain (constrain) A pinch and a pan.

    💡 can be thought of as a general-purpose scaling constraint that can place complex constraints on transformation operations.

    Its input parameter is a function that takes three arguments in turn and returns a constrained transformation value:

    • transformThe scale transformation object that should have been performed
    • extentScope of view
    • translateExtentRange of translation

    The default constraint function is the following, which ensures that the view range is no larger than the panning range so that the user can move parts of the image anywhere in the view

    function constrain(transform, extent, translateExtent) {
      var dx0 = transform.invertX(extent[0] [0]) - translateExtent[0] [0],
          dx1 = transform.invertX(extent[1] [0]) - translateExtent[1] [0],
          dy0 = transform.invertY(extent[0] [1]) - translateExtent[0] [1],
          dy1 = transform.invertY(extent[1] [1]) - translateExtent[1] [1];
      return transform.translate(
        dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
        dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
      );
    }
    Copy the code
  • Zoom. extent([extent]) Sets the view scope viewport extent.

    The extent parameter is an array [[x0, y0], [x1, y1]], where [x0, y0] represents the upper-left corner of the view scope and [x1, y1] represents the lower-right corner of the view scope.

    The extent argument can also be a function that returns an array, called in turn by the elements in the selected set, passing as an argument the data bound to the currently traversed elements, datum d. The this inside the function refers to the element node being traversed.

    💡 If the zoomer is bound to a normal HTML element, the default value of the view scope [[0, 0], [width, height]] is the same size as the element’s width and height; If the zooler is bound to an element of type SVG, the default value for the view scope is the viewBox of SVG (if the viewBox property is not set, the width and height of SVG, i.e., viewPort, is used).

    Viewport extent affects some functions, such as transformations triggered by zoom.scaleby () and zoom.scaleto (), where the center of the view remains the same. The center and size of a view affect the path of a transition animation created using the interpolator d3.interpolateZoom; The translate extent constraint depends on the view scope (the translate extent should be larger than the view scope).

  • Zoom. TranslateExtent ([extent]) Set the translateExtent to translate extent.

    The extent parameter is an array [[x0, y0], [x1, y1]], where [x0, y0] represents the upper-left corner of the pan range and [x1, y1] represents the lower-right corner of the pan range. The default is [[-∞, -∞], [+∞, +∞]].

    💡 Although this method is limited to translation operation, it may cause the occurrence of translation when shrinking.

    ⚠️ This constraint takes effect when a transformation is performed using zoom.scaleby (), zoom.scaleto (), zoom.translateby (); The zoom. Transform () method does not take effect

The two special scopes mentioned in the above three methods are viewPort extent view scope and translate extent translation scope. You can indirectly limit where elements can be panned by setting the size of the view’s range and by limiting where the view (range, an array) can be modified by panning the range. The combination of these two special ranges allows you to keep certain elements from being moved out of the frame.

  • Zoom. scaleExtent([extent]) Sets the zoom scope. The extent parameter is an array [k0, k1] that represents the scale range, where k0 is the minimum scale that can be set and k1 is the maximum scale that can be set. The default range is [0,∞][0,\infty][0,∞]

    If the constraint’s scaling limit is reached, the scaling is ignored even if the user continues to scroll the mouse wheel.

    💡 The above method limits the zoom of the view, but may cause a “side effect”, that is, when the view reaches the zoom limit of the constraint, the user continues to scroll, causing the page to scroll (if the page is scrollable at that time). If you want to fix this “side effect”, You can listen for the wheel event on the appropriate selection set and cancel its default behavior

    selection
      .on(zoom) Cancel the default behavior of the wheel event after setting the zoom listener for the elements of the selection set
      .on("wheel".event= > event.preventDefault());
    Copy the code

    ⚠️ This constraint takes effect when a transformation is performed using zoom.scaleby (), zoom.scaleto (), zoom.translateby (); The zoom. Transform () method does not take effect

  • Zoom. filter[(filter)] Is used to determine whether zooming is performed. The filter argument is a function that returns a Boolean value, ignoring the transform if falsy is returned. It is used to restrict not responding to scaling operations under certain conditions.

    The filter function takes as arguments the current zoom event event and the data datum d bound to the element of the selection set that is currently calling the zoomer, and this within the function points to the current element node.

    The default values are as follows, so when you press Ctrl (but you can press Ctrl while rolling the mouse pulley) or use the secondary button of the mouse (for right-handed users, the secondary button usually means the right button of the mouse), zooming and panning is not possible by default, because these actions are usually used for other purposes

    function filter(event) {
      // For the mouse set to right-handed operation
      // When left-clicking, event.button is 0
      // When using the right button, event.button is 2
      return(! event.ctrlKey || event.type ==='wheel') && !event.button;
    }
    Copy the code
  • Zoom.wheeldelta ([delta]) sets the delta value for each scroll of the mouse wheel. The parameter delta is a function and finally returns the modified delta value δ \ delta δ.

    The default values of parameter delta are as follows

    function wheelDelta(event) {
      return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002);
    }
    Copy the code

    This value is used to calculate the scale k×2 δ k\times 2^{\Delta}k×2 δ (where KKK is the original scale) with the mouse wheel. For example, when δ =1\Delta =1 δ =1, the elements of the view are reduced by half; When δ =−1\Delta = -1 δ =−1, the elements of the view are doubled in size

  • Zoom.clickdistance ([distance]) sets the maximum allowed distance between clicking event and releasing mouse (the distance is calculated by clicking event. ClientX and event.clienty). If the distance is greater than or equal to this distance, It doesn’t throw a click event.

    💡 can be imagined with the mouse-down point as the center of the circle and the parameter distance as the radius, releasing the mouse inside the circle will throw a click event, while releasing the mouse outside the circle (or on the circle) will ignore the click event (because the drag event should be triggered more at this time). The default value of distance is 0

    This method can be used to optimize the scene of click enlargement. However, there are a large number of small elements in the original screen, and small movement may occur from mouse press down to release, so as to avoid being identified as the dragging operation of this element. Click events and drag events can be distinguished by setting the “maximum distance of movable click”.

  • Zoom.tapdistance ([distance]) for the touch screen device, The maximum distance allowed for two clicks (calculated from the event. ClientX and event. ClientY of the first click on touchStart and the second click on touchend), if greater than or equal to this distance, the DBLclick double click event will not be thrown.

    The application scenario of 💡 is similar to the previous method. It is generally used to distinguish double-click events from drag-and-drop events. The default parameter distance is 10