Day11-D3 Mouse Event 滑鼠事件

本篇大纲:click、hover、mouseover、mousemove

由於 D3.js 是操作DOM元素去建构图表,因此DOM元素能使用的触发事件,D3也一样能够使用~我们现在就来看看该怎麽使用d3的事件,以及有哪些事件可以用吧!

selection.on(事件, function)

想触发d3.js的事件要使用的 API 是 selection.on( ),这个方法一样归纳在selection之下,因为我们要先选定节点,才能将事件绑定到这个节点上。

selection.on( ) 能使用所有DOM元素原生的事件,而一般来说最常用的有:

  • click
  • mouseover、mousemove、mouseleave

这些事件能结合上一篇介绍的 .transition( ) 做出各种有趣的效果,例如:

click:点击方块时,它会移动位置并改变颜色

// html
<svg class="event"></svg>

// js 
// rect 
d3.select('.event')
  .append('rect')
  .attr('class', 'rect')
  .attr('width', 30)
  .attr('height', 30)
  .attr('fill', 'blue')
  .append('rect')

// 加上事件
d3.select('.rect')
  .on('mouseover', function(){
      d3.select(this)
        .transition()
        .attr('cursor', 'pointer')
        .attr('fill', 'green')
        .attr('transform', 'translate(250, 0)')
})

https://i.imgur.com/UGYcGS5.gif

mouseover、mouseleave:滑鼠滑过时改变位置与颜色,滑鼠离开时回到原本状态

// html
<div class="chartContainer">
    <div class="box1">
        <p>aa</p>
        <p>aa</p>
        <p>aa</p>
        <p>aa</p>
    </div>
</div>

// js
// hover (mouseover / mouseleave)
d3.selectAll('p')
    .on('mouseover', function () {
        d3.select(this)
          .style("color", "red")
          .transition()
          .style('cursor', 'pointer')
          .style("transform", "translate(50px)");
    })
    .on("mouseleave", function () {
        d3.select(this).style("color", "black")
            .transition().style("transform", "translate(0)");
    });

https://i.imgur.com/FodOYuN.gif

但光是会使用DOM的事件还不够,大部分的时候一个完整的图表会包含许多DOM元素,我们需要知道各别DOM元素在图表中的位置,才能正确选定想操作的DOM元素,例如:
https://i.imgur.com/x0F1EJS.gif

这个时候,我们就要运用 d3.pointer( ) 这个方法了

d3.pointer(事件, target)

以前想取得DOM节点的座标轴时,可以根据不同事件去找对应的方法,例如:使用 d3.mouse、d3.touch、 d3.touches、d3.clientPoint 等等;但後来 d3 在第六版时进行了一些调整,把这些方法全部合并到 d3.pointer,再透过带入参数的方式去指定想触发的事件,想更深入了解的人可以看看这个 v5~v6 Migration Guide

现在,我们一样先来看到 d3.pointer( )的官方文件介绍

https://ithelp.ithome.com.tw/upload/images/20210923/20134930N5vfo8Kd79.jpg

d3.pointer(事件, target) 这个方法会根据所带入的事件,返还指定target的 [x,y]座标轴,我们就可以透过这个座标轴去抓到指定的DOM元素。

先来个小范例看看要怎麽使用吧!

范例:滑鼠移动时,显示目前滑鼠所在的座标轴

首先,我们先建立svg视窗大小,接着绑定 mousemove 的事件

// html
<h5 class="mt-5">3. pointer()找出目前滑鼠所在 X Y 轴座标</h5>
<div class="position"></div>

// js 
// 先建立svg并设定大小
const svg = d3.select('.position').append('svg');
svg.attr('width', 500)
   .attr('height', 500);

// 设定svg滑鼠事件
svg.on('mousemove', function () {

});

接着我们使用 d3.pointer()的方法带入目前事件与要操作的 node 结点,并把返还的数值console出来看看

// 设定svg滑鼠事件
svg.on('mousemove', function () {
   let pt = d3.pointer(event, svg.node())
	 console.log(pt)
});

返还的数值是一个阵列,内含的两笔资料分别代表X跟Y轴的座标
https://ithelp.ithome.com.tw/upload/images/20210923/20134930vzRMm9knQG.jpg

最後我们简单设定一个 tooltip 来呈现滑鼠当下的座标轴吧!详细的tooltip设定下一篇会讲解,这边大家就稍微看一下程序码就好~

//pointer()、tooltip
let txt = svg.append("text");
svg.on('mousemove', function () {
        //d3.pointer 会回传阵列[X,Y]
        let pt = d3.pointer(event, svg.node())
        txt.attr('x', pt[0]) //取[x]
            .attr('y', pt[1]) //取[Y]
            .text(`X:${parseInt(pt[0])} | Y:${parseInt(pt[1])}`)
        console.log(pt)
    });

结果如下~滑鼠移动到哪,就会显示当下的座标轴
https://i.imgur.com/EOVmrP9.gif

这下就可以玩很多花样啦~我们结合d3的画面、事件、座标轴、动画来玩玩看看吧!

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

//js
const data = [130, 210, 90, 250]
const combined = d3.select('.combined')
                  .append('svg')
                  .attr('width', 500)
                  .attr('height', 300);

const dots = combined.selectAll('circle')
                     .data(data)
                     .enter()
                     .append('circle')
                     .attr('cx', d=>d)
                     .attr('cy', (d,i)=>(i+1)*60)
                     .attr('r', '15')
                     .attr('fill', 'blue')
                     .attr('cursor', 'pointer')

// 绑定事件
dots.on('mouseover', function(){
    let pt = d3.pointer(event, event.target)
    d3.select(this)
      .attr('fill', 'red')
      .transition()
      .attr('cx', pt[0]+100)

});

https://i.imgur.com/drWtUiY.gif

是不是很有趣呢?d3 的事件部分就讲到这边,之後的图表就能搭配事件跟动画,做出一连串酷炫的效果啦~


Github Page 图表与 Github 程序码

这边附上本章的程序码与图表 GithubGithub Page,需要的人请自行取用~


<<:  【没钱买ps,PyQt自己写】Day 8 – 我们的第二个 input 手段 – QLineEdit

>>:  Day11 - 模型与管理网站

DAY 8 『 CollectionView 』Part1

CollectionView:Storyboard、Xib + Collection View + ...

[Day 08] 原形设计的样板参考

由於不是设计师,对於UI/UX相关的东西懂得可能就跟完全没接触过的人一样, 所以在想做一个自己的Ap...

硬碟管理原理

unRaid系统会要求使用者要先配置硬碟,才能使用各个功能(如VM,共享资料夹…等) 今天先深入了解...

Day 27:开始撰写 Playbook

今天努力了一个下午,终於算是勉强搞出了一组能动的 playbook,这边就来记录一下过程以及就我所知...

2.4.10 Design System - Input Text

以前我绝对是对哲学避之唯恐不及的 但某一年意外看到「正义 一场思辨之旅」以及「超译 尼采」後 开始...