D3.js 交互

交互(Interaction)

D3 提供了许多模块来帮助您添加交互性,例如缩放、平移和画笔。

  • 四叉树(Quadtrees):通过在每次移动鼠标时搜索最接近鼠标指针的项目,可以更轻松地挑选小项目。这可能是一项昂贵的操作,但使用 D3 的四叉树模块可以提高效率。四叉树是一种树数据结构,它递归地将一个区域划分为越来越小的区域,并且可以使搜索项目更有效。

    let data = [], width = 600, height = 400, numPoints = 100;
    
    let quadtree = d3.quadtree()
      .x(function(d) {return d.x;})
      .y(function(d) {return d.y;});
    
    let hoveredId;
    
    function updateData() {
      data = [];
      for(let i=0; i<numPoints; i++) {
        data.push({
          id: i,
          x: Math.random() * width,
          y: Math.random() * height,
          r: 1 + Math.random() * 20
        });
      }
    }
    
    function handleMousemove(e) {
      let pos = d3.pointer(e, this);
      let d = quadtree.find(pos[0], pos[1], 20);
      hoveredId = d ? d.id : undefined;
      update();
    }
    
    function initEvents() {
      d3.select('svg')
        .on('mousemove', handleMousemove);
    }
    
    function updateQuadtree() {
      quadtree.addAll(data);
    }
    
    function update() {
      d3.select('svg')
        .selectAll('circle')
        .data(data)
        .join('circle')
        .attr('cx', function(d) { return d.x; })
        .attr('cy', function(d) { return d.y; })
        .attr('r', function(d) { return d.r; })
        .style('fill', function(d) { return d.id === hoveredId ? 'red' : null;});
    }
    
    updateData();
    updateQuadtree();
    update();
    initEvents();
    
  • 德劳内三角形(Delaunay triangles):还可以使用 D3 的 Delaunay 模块来查找最近点。给定一个点数组,Delaunay 三角剖分将所有点与三角形连接起来,从而使碎片最小化。四叉树不同,不支持添加最大距离作为第三个参数。

    let data = [], width = 600, height = 400, numPoints = 100;
    let triangles;
    let hoveredId;
    
    function updateData() {
      data = [];
      for(let i=0; i<numPoints; i++) {
          data.push({
              id: i,
              x: Math.random() * width,
              y: Math.random() * height,
              r: 1 + Math.random() * 20
          });
      }
    }
    
    function handleMousemove(e) {
      let pos = d3.pointer(e, this);
      let i = triangles.find(pos[0], pos[1]);
      hoveredId = data[i].id;
      update();
    }
    
    function initEvents() {
      d3.select('svg')
          .on('mousemove', handleMousemove);
    }
    
    function updateDelaunay() {
      triangles = d3.Delaunay
          .from(data,
                function(d) {return d.x;},
                function(d) {return d.y;}
               );
    }
    
    function update() {
      d3.select('svg')
          .selectAll('circle')
          .data(data)
          .join('circle')
          .attr('cx', function(d) { return d.x; })
          .attr('cy', function(d) { return d.y; })
          .attr('r', function(d) { return d.r; })
          .style('fill', function(d) { return d.id === hoveredId ? 'red' : null;});
    }
    
    initEvents();
    updateData();
    updateDelaunay();
    update();
    
  • 拖动(Dragging):D3 有一个用于向元素添加拖动行为的模块。D3 的拖动模块也支持触摸手势。使 HTML/SVG 元素可拖动需要三个步骤:

    • 调用d3.drag()创建拖动行为函数
    • 添加一个在拖动事件发生时调用的事件处理程序。事件处理程序接收一个事件对象,您可以使用它来更新拖动元素的位置
    • 将拖动行为附加到要使其可拖动的元素

    拖动事件对象有几个属性,其中最有用的是:

    属性名称 描述
    .subject 被拖动元素的连接数据
    .x&.y 被拖动元素的新坐标
    .dx&.dy 被拖动元素的新坐标,相对于之前的坐标
    let data = [], width = 600, height = 400, numPoints = 10;
    
    let drag = d3.drag()
      .on('drag', handleDrag);
    
    function handleDrag(e) {
      e.subject.x = e.x;
      e.subject.y = e.y;
      update();
    }
    
    function initDrag() {
      d3.select('svg')
        .selectAll('circle')
        .call(drag);
    }
    
    function updateData() {
      data = [];
      for(let i=0; i<numPoints; i++) {
        data.push({
          id: i,
          x: Math.random() * width,
          y: Math.random() * height
        });
      }
    }
    
    function update() {
      d3.select('svg')
        .selectAll('circle')
        .data(data)
        .join('circle')
        .attr('cx', function(d) { return d.x; })
        .attr('cy', function(d) { return d.y; })
        .attr('r', 40);
    }
    
    updateData();
    update();
    initDrag();
    
  • 刷牙(Brushing):允许用户指定一个区域(通过按下鼠标按钮,移动鼠标,然后释放),例如,选择一组元素。将画笔行为添加到 HTML 或 SVG 元素需要三个步骤:

    • 调用d3.brush()创建画笔行为函数
    • 添加一个在发生画笔事件时调用的事件处理程序。事件处理程序接收画笔范围,然后可用于选择元素、定义缩放区域等。
    • 将画笔行为附加到一个元素(或多个元素)
    let brush = d3.brush()
      .on('start brush', handleBrush);
    
    function handleBrush(e) {
      // Use the brush extent e.selection to compute, for example, which elements to select
    }
    
    function initBrush() {
      d3.select('svg')
        .call(brush);
    }
    
    initBrush();
    

    事件类型:'brush'和'start'和'end'。'brush'表示画笔范围已更改。'start'表示刷牙已经开始(例如用户按下了鼠标按钮)。'end'表示刷牙结束(例如用户已松开鼠标按钮)。

    handleBrush接收单个参数e,该参数是表示画笔事件的对象。画笔事件中最有用的属性.selection是将画笔的范围表示为一个数组[[x0, y0], [x1, y1]],其中x0, y0和x1, y1是画笔的对角。通常handleBrush会计算哪些元素在画笔范围内并相应地更新它们。

    通过选择元素并将画笔行为传递给.call方法,可以将画笔行为附加到元素:

    //完整例子
    let data = [], width = 600, height = 400, numPoints = 100;
    
    let brush = d3.brush()
      .on('start brush', handleBrush);
    
    let brushExtent;
    
    function handleBrush(e) {
      brushExtent = e.selection;
      update();
    }
    
    function initBrush() {
      d3.select('svg g')
        .call(brush);
    }
    
    function updateData() {
      data = [];
      for(let i=0; i<numPoints; i++) {
        data.push({
          id: i,
          x: Math.random() * width,
          y: Math.random() * height
        });
      }
    }
    
    function isInBrushExtent(d) {
      return brushExtent &&
        d.x >= brushExtent[0][0] &&
        d.x <= brushExtent[1][0] &&
        d.y >= brushExtent[0][1] &&
        d.y <= brushExtent[1][1];
    }
    
    function update() {
      d3.select('svg')
        .selectAll('circle')
        .data(data)
        .join('circle')
        .attr('cx', function(d) { return d.x; })
        .attr('cy', function(d) { return d.y; })
        .attr('r', 4)
        .style('fill', function(d) {
          return isInBrushExtent(d) ? 'red' : null;
        });
    }
    
    initBrush();
    updateData();
    update();
    

    D3 还提供了画笔d3.brushX和d3.brushY将画笔限制在一个维度上。

    画笔行为有两种设置画笔范围.move和.clear。第一个设置画笔范围,第二个清除画笔。

    d3.select('svg g')
      .call(brush.move, [[50, 50], [100, 100]]);
    
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 过渡(Transitions) D3 过渡可以在不同图表状态之间平滑地制作动画。 创建D3过渡:在要转换的和方法之...
    亿个小目标阅读 727评论 0赞 1
  • <!DOCTYPE html> 111 const oriData = [ {'x':'A计...
    灵魂旅者s阅读 1,382评论 0赞 0
  • Data Visualization with D3 D3: SVG中的jQurey 1. Add Documen...
    王策北阅读 917评论 0赞 2
  • 层次结构(Hierarchies) 分析或可视化数据时的一种常用技术是将数据组织成组。(分组) 可以将层次结构视为...
    亿个小目标阅读 1,246评论 0赞 1
  • 近期在做线路图实时刷新的功能,用到的技术主要有d3、svg、websocket。整体思路就是解析线路图json,使...
    淼一___阅读 1,262评论 0赞 1

友情链接更多精彩内容