Day16-D3 的 Brush 刷子

本篇大纲:d3.brush( )、brush 的 API 们、范例

今天我们要来看本系列的最後一个互动效果,那就是 — Brush 刷子啦!Brush 的功能是用来选取(刷取) 图表上的区间段,接着再看我们想对此区间段内的元素做什麽事 (变色、移位、放大缩小等等)。基本上的效果会像这个样子,长方形的部分就是我们用 brush 选起来的区间~

https://ithelp.ithome.com.tw/upload/images/20210928/20134930q7xNkb5oWP.jpg

了解 brush 怎麽运作之後,我们现在来看看官方文件提供哪些 API 给 brush这个功能吧!由於 d3.brush 主要只是用来建立选取区间,相对来说是个容易一点的功能,因此 D3 提供来处理 brush 效果的 API 们也比较少,以下就是全部的 API

https://ithelp.ithome.com.tw/upload/images/20210928/20134930khqP6V00KZ.jpg

下面我一样会介绍几个比较常用的API,想了解更多的人欢迎自行上官网查看~

d3.brush( )、d3.brushX( )、d3.brushY( )

我们先来看看最主要用来建立 brush 的方法 — d3.brush( )。当我们使用 d3.brush( )时,它会建立一个二维 brush 并自动带入svg的滑鼠跟触控事件,我们就能用滑鼠或手指触控来进行操作。

除了使用d3.brush( ) 之外,如果你只想建立一维X轴向的brush,可以用 d3.brushX( ) 这个API;反之如果想建立一维Y轴向的brush,则是使用d3.brushY( )。

建立好brush之後,我们接着使用 selection.call( )的方法把建立好的 brush 绑定到选定的DOM元素上,然後就可以使用 brush.on(事件,方法) 来监听 brush 事件

svg.append("g")
    .attr("class", "brush")
    .call(d3.brush().on("brush", brushed));

brush.on(事件, 方法 )

我们一样先看到官方文件的解说:
https://ithelp.ithome.com.tw/upload/images/20210928/20134930AXV1zZNe3I.jpg

这个方法是用来监听 brush 事件,而 brush 事件又可以分成三种

  • start
  • brush
  • end

因此,我们可以针对不同的事件使用不同的方法

svg.append("g")
    .attr("class", "brush")
    .call(d3.brush()
            .on("start brush", brushed)
						.on('end' brushEnd)
          )

一旦我们开始监听 brush event 之後,每个 event 就会包含以下几种属性
https://ithelp.ithome.com.tw/upload/images/20210928/20134930tBEKT0YyrU.jpg

  • target - 触发brush 行为的元素
  • type -目前的事件为何,例如 start、brush、end
  • selection - 目前绑定node节点的 brush 选取集合。这个集合一般是包含数字的阵列,如果brush是二维,此集合的属性值会是[[x0, y0], [x1, y1]]。我们会运用 event.selection 来判断DOM元素是否在brush的范围内
  • sourceEvent - 原生事件,例如 mousemove 或 touchmove
  • mode - brush当下的状态,例如 drag、space、handle、center 等等.

brush.extent([[x0, y0], [x1, y1]])

这个方法是用来设定可以刷取的范围,[x0, y0] 是设定左上角位置,[x1, y1]则是设定右下角位置。一般来说会设定的跟svg一样大或是稍微大一点

svg.append("g")
    .attr("class", "brush")
    .call(d3.brush()
            .extent( [ [0,0], [width, height] ])
            .on("start brush", brushed)
          )

brush.handleSize([size])

这个方法则是用来设置 brush 把柄的大小,这个大小则会决定如果没有特别设定的话,预设的尺寸是6。但这个方法必须要在用selection.call( ) 呼叫并绑定brush 之前就使用。


实际范例

看完上面brush能使用的方法後,我们直接来实际操作看看吧!首先,我们在画面上建立两个圆点

// html
<div class="chartContainer"></div>

// js
const data = [{r:20, x:200, y:120},{r:35, x:350, y:280}]

// svg
const svg = d3.select('.chartContainer')
        .append('svg')
        .attr('width', 500)
        .attr('height', 500);

// 放上点点
const dots = svg.selectAll('circle')
                .data(data)
                .enter()
                .append('circle')
                .attr('r', d => d.r)
                .attr('cx', d => d.x)
                .attr('cy', d => d.y)
                .style("fill", "#19d3a2")

现在的画面是这样
https://ithelp.ithome.com.tw/upload/images/20210928/20134930567rYsvAuj.jpg

接着,我们来建立brush吧~由於我们希望被brush选到後圆点点会变色,因此我们在css 设定一个 selected 来处理变色的事情,然後在刷子运作时把这个class赋予到圆点上

// css 档案 或 style 加上
.selected{
  fill: blue !important
}

// 加上brush
svg.append("g")
.attr("class", "brush")
.call(d3.brush()
        .extent( [ [0,0], [600,600] ]) // extent限制刷子的活动区块,理想是比画布稍大
        .on("start brush", brushed) // brush 事件
      )

// brush 的功能
// 加上event参数,就能用 event.selection
// selection会产出一个二维阵列,分别代表`x0`, `x1`, `y0`, `y1`,左上到右下的位置,让你有办法重新计算目前位置的extent,进而进行其他操作。
function brushed(event){
    console.log(event)
    const extent = event.selection
    dots.classed('selected', d => {return isBrushed(extent, d.x, d.y)})
}

接下来,因为画面上有两个点点,我们要设定刷子的范围刷到哪个圆点时,圆点才要变色,因此我们要设定isbrushed的方法确认圆点是否在brush选到的区块内

// 设定圆点是否在brush选到的区块内
function isBrushed(brush_coors, cx, cy){
  let x0 = brush_coors[0][0],
      x1 = brush_coors[1][0],
      y0 = brush_coors[0][1],
      y1 = brush_coors[1][1]

    // 如果圆点在brush的范围内,就会传true;反之则回传false
    console.log(x0 <= cx && cx <= x1 && y0 <= cy && cy <= y1)
    return x0 <= cx && cx <= x1 && y0 <= cy && cy <= y1; 
}

这样就完成罗~
https://i.imgur.com/BJq4vYI.gif

除了改变元素的颜色之外,brush 效果通常是用来搭配 zoom 效果进行图表的缩放,不过这个要用完整图表来示范比较清楚,所以我们就留到之後的范例来解讲吧

终於讲完 D3 全部的的互动效果了(累倒),明天开始我们就要进入另一个大重点:轴线跟比例尺!学会了之後就可以画各种想画的图表啦~~


Github Page 图表与 Github 程序码

最後一样附上本章的程序码与图表 GithubGithub Page,需要的人请自行取用~


<<:  Day23:Hot Flow - StateFlow

>>:  学习历程救援事件(灾难复原实例)後续追踪

Day27 测试写起乃 - logger level

如果不想看到一堆 SQL 的 log,有另一种方式可以清除,就是调整 logger level 看官...

Day5 - 2D渲染环境基础篇 II - 成为Canvas Ninja ~ 理解2D渲染的精髓

何谓路径? 要介绍路径绘图相关的api之前,必须要先理解什麽叫做『路径』。 有学过电脑绘图软件,例如...

Day 29 - Rancher Fleet Helm + Kustomize 应用程序部署

本文将於赛後同步刊登於笔者部落格 有兴趣学习更多 Kubernetes/DevOps/Linux 相...

DAY17:清单元件之实作

今天要给大家看实例,接下来用图片介绍。 首先我们先在主画面建立三个清单元件 以及新增两个客制化的画面...

Day-25 尚未开始便已衰败、策略错误的 XBOX ONE

为了与 SONY 的 PS4 相抗衡、基於过去的策略、微软也选择在几乎相同的时间发布了他们的新主机 ...