添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

本文主要介绍 Zooming 模块

Panning 平移和 Zooming 缩放是一种常见的交互,通过点击和拖拽可以对图像进行平移(相应地修改图形的 CSS translate 样式属性),通过滚动鼠标的滚轮/(在触屏设备上)双指捏放可以对图像进行缩放(相应地修改图形的 CSS scale 样式属性),让使用者在有限的视图中聚焦探索感兴趣的部分。

d3-zoom 模块提供简单且灵活的方式实现对 SVG 、HTML 或 Canvas 的平移和缩放,除了可以操作选择集,还支持对比例尺和坐标轴的缩放平移。该模块也提供相应的方法,可以通过编程的方式实现缩放平移,还可以为此设置 顺滑的过渡动画

创建缩放器

使用方法 d3.zoom() 创建一个缩放器(以下称为 zoom

它既是一个方法,可以接收选择集作为参数 zoom(selection) ,为选择集中的元素(一般是包含数据的容器 <g> 元素)添加相应的缩放事件监听器,并为它们设置变换 transform 的初始值

// 一般通过 selection.call() 方法调用缩放器创建函数
// 这样 selection 选择集就会作为参数传递给缩放器创建函数
selection.call(d3.zoom().on("zoom", zoomed));

以上示例中,缩放器为选择集中的元素(容器 <g>)添加(D3 所定义的缩放事件类型)zoom 事件监听器,并设置了相应的处理函数 zoomed(一般在回调函数中进行 transform 相关样式属性的修改,实现元素在特定视图内的缩放平移,一般应用于容器 <g> 元素,这样容器的内的数据整体都会跟随着缩放平移了)

💡 如果希望移除缩放相关事件的监听器,可以为将相应事件回调函数设置为 null

selection.on(".zoom", null);
// 也可以只移除特定的缩放事件的监听器,如滚轮缩放
selection.on("wheel.zoom", null);

它也是一个对象,提供多种与缩放变换相关的方法,如通过设置监听器用户触发的原生点击拖拽等事件,以触发缩放变换;也可以调用相应的方法以编程的方式来执行缩放;还可以通过相应的方法来约束缩放变换行为。

zoom.constrain([constrain]) 对缩放平移操作进行约束。

💡 可以将它视为通用的缩放约束方法,可以对变换操作进行复杂的限制。

其入参是一个函数,该函数依次接收三个参数,最后返回一个经过约束的变换值:

  • transform 原本需要执行的缩放变换对象
  • extent 视图范围
  • translateExtent 平移的范围
  • 默认的约束函数如下,其作用是确保视图范围不大于可平移的范围,这样用户就可以通过移动,将图像的各个部分移到视图的任何地方

    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)
    

    zoom.extent([extent]) 设置视图范围 viewport extent。

    参数 extent 是一个数组 [[x0, y0], [x1, y1]],其中 [x0, y0] 表示视图范围的左上角,而 [x1, y1] 表示视图范围的右下角。

    参数 extent 也可以是一个返回数组的函数,它会被选择集中的元素依次调用,并传递当前所遍历的元素所绑定的数据 datum d 作为参数。而函数内的 this 指向当前所遍历的元素节点。

    💡 如果缩放器绑定的是普通的 HTML 元素,则视图范围的默认值 [[0, 0], [width, height]] 即与元素的宽高大小相同;如果缩放器绑定的是 SVG 类型的元素,则视图范围的默认值是 SVG 的 viewBox(如果没有设置 viewBox 属性,就使用 SVG 的宽高,即 viewport)。

    视图的范围 viewport extent 对于一些函数有影响,如通过 zoom.scaleBy()zoom.scaleTo() 方法触发的变换,其视图的中心会保持不变;视图中心和大小会影响使用插值器 d3.interpolateZoom 创建的过渡动画的路径;而平移范围 translate extent 的约束需要依赖视图范围(平移范围应该大于视图范围)。

    zoom.translateExtent([extent]) 设置平移范围 translate extent。

    参数 extent 是一个数组 [[x0, y0], [x1, y1]],其中 [x0, y0] 表示平移范围的左上角,而 [x1, y1] 表示平移范围的右下角。默认值是 [[-∞, -∞], [+∞, +∞]]

    💡 该方法虽然约束的是平移操作,但可能造成缩小时平移的发生。

    ⚠️ 该约束在通过 zoom.scaleBy()zoom.scaleTo()zoom.translateBy() 方法执行变换时生效;但是通过 zoom.transform() 方法执行变换时生效

    以上三个方法提及的两个特殊的范围:viewport extent 视图范围、translate extent 平移范围。通过设置视图范围的大小,以及通过平移范围来约束视图(范围,一个数组)可以修改的位置,可以间接来限制元素可以平移的位置,通过这两个特殊范围的配合可以实现特定的元素不被移出画面外这一需求

    zoom.scaleExtent([extent]) 设置缩放比例的范围。参数 extent 是一个的数组 [k0, k1],表示缩放比例的范围,其中 k0 是可设置的最小缩放比例,k1 是可设置的最大缩放比例。默认范围是 [0,][0,\infty ]

    如果达到了约束的缩放比例极限时,即使用户继续滑动鼠标滚轮,缩放变换也会被忽略。

    💡 以上方法限制视图的缩放,但可能会造成一个「副作用」,即当视图缩放达到了约束的缩放比例极限时,用户还继续滚动就会造成页面的滚动(如果当时页面是可滚动的),如果希望修正这个「副作用」,可以在相应的选择集上监听 wheel 事件并取消它的默认行为

    selection
      .on(zoom) // 为选择集的元素设置了缩放监听器后,取消 wheel 事件的默认行为
      .on("wheel", event => event.preventDefault());
    

    ⚠️ 该约束在通过 zoom.scaleBy()zoom.scaleTo()zoom.translateBy() 方法执行变换时生效;但是通过 zoom.transform() 方法执行变换时生效

    zoom.filter[(filter)] 用于判断是否执行缩放变换操作。参数 filter 是一个返回布尔值的函数,当返回的是 falsy 时忽略变换操作。它用以限制特定条件下不响应缩放变换操作。

    函数 filter 接收当前的缩放事件 event 和当前调用缩放器的选择集的元素所绑定的数据 datum d 作为参数,而函数内的 this 指向当前元素节点。

    其默认值如下,因此按下 Ctrl(但是可以在滚动鼠标滑轮时按下 Ctrl)或使用鼠标的次级按键(对于右手用户,次级按键一般是指鼠标的右键)时,默认是无法进行缩放平移操作,因为这些操作一般有其他用途

    function filter(event) {
      // 对于设置为右手操作的鼠标
      // 当使用左键时,event.button 为 0
      // 当使用右键时,event.button 为 2
      return (!event.ctrlKey || event.type === 'wheel') && !event.button;
    

    zoom.wheelDelta([delta]) 设置鼠标滚轮的每次滚动的 delta 值,参数 delta 是一个函数,最后返回修改后的 delta 值 Δ\Delta

    参数 delta 默认值如下

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

    该值用于在鼠标滚轮时计算缩放比例 k×2Δk\times 2^{\Delta}

    zoom.clickDistance([distance]) 设置点击事件中,鼠标按下与放开鼠标之间允许的最大距离(该距离通过点击事件的 event.clientXevent.clientY 计算得到),如果大于等于该距离,就不会抛出点击事件。

    💡 可以想象以鼠标按下点为圆心,以参数 distance 为半径,在该圆内释放鼠标,都会抛出点击事件,在圆外(或圆周上)放开鼠标,点击事件都会被忽略(因为此时更应该触发拖拽事件)。参数默认值 distance0

    该方法可用于优化点击放大的场景,而原始画面中有大量较小的元素,从鼠标的按下到释放可能会发生微小的移动,避免识别为对该元素的拖拽操作,可以通过设置「可移动式点击的最大距离」,来区分点击事件和拖拽事件。

    zoom.tapDistance([distance]) 对于触屏设备在双击时,两次点击允许的最大距离(该距离通过首次点击的 touchstart 和第二次点击的 touchend 事件的 event.clientXevent.clientY 计算得到),如果大于等于该距离,就不会抛出 dblclick 双击事件。

    💡 其应用场景和前一个方法类似,一般是为了区分双击事件和拖拽事件,参数默认值 distance10

    JavaScript