iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 23
0
Modern Web

資料視覺化!D3入門到實戰系列 第 23

Day23 實戰!Github Heat Map 產生器_加上hover資料提示

  • 分享至 

  • xImage
  •  

資料視覺化的核心是看見趨勢、概況,而不是某一個節點的細節資料,所以在圖表設計上多半不會直接在每個資料點上標示數字,但還是有其必要性,表現的方式就是以tooltip的形式呈現。

邊框加粗加深

為了讓滑鼠hover的效果更強烈、明顯,我們幫方塊加上2px的邊線,與深一點的顏色。什麼是「深一點的顏色呢」?還記不記得之前有介紹過d3提供跟color有關的函式們:https://github.com/d3/d3-color ,在裡面就有提供darker()的功能,並加入參數適當調深,我們立刻來試試看。

g
    .selectAll('.block')
    .data(data)
    .enter()
    .append('rect')
    .attr('class', 'block')
    .attr('x', (d) => (d.weekNum - 1) * 15 + (d.weekNum - 1) * 1 + 7.5)
    .attr('y', (d) => d.day * 15 + d.day * 1 + 7.5)
    .attr('fill', (d) => colorScale(0))
    .attr('width', 0)
    .attr('height', 0)
    
    //只有加上這幾行
    .on('mouseover', function(d) {
      d3.select(this)
        .attr('stroke', d3.rgb(colorScale(d.value)).darker(0.5))
        .attr('stroke-width', 2);
    }) // 當mouseover時加上邊框,邊框顏色為本體顏色加深0.5
    .on('mouseleave', function() { //注意!這裡不能使用arrow function 因為我們要選擇的是this
      d3.select(this)
        .attr('stroke-width', '0');
    }) // 當mouseleave時恢復原樣
    
    
    .transition()
    .duration(1000)
    .delay((d, i) => i * 3)
    .attr('fill', (d) => colorScale(d.value))
    .attr('x', (d) => (d.weekNum - 1) * 15 + (d.weekNum - 1) * 1)
    .attr('y', (d) => d.day * 15 + d.day * 1)
    .attr('width', 15)
    .attr('height', 15)
    .attr('rx', 3);

資料提示tooltip

與之前相同,我們直接用div來做提示框。react不像vue一樣css寫在同一份檔案,所以在同一層的資料夾下新增一份css或scss的檔案:HeatMap.module.scss,這邊加上module的話就會被認為是local style,react在編譯時會幫你加上一些id,就不會有css重複命名的問題,要注意的是要記得把這份style傳給畫圖的function。

HeatMap.module.scss

.container {
  position: relative;
}

.messageWrapper {
  border-radius: 4px;
  width: 125px;
  height: 34px;
  background: #ffffff;
  box-shadow: 0 2px 9px 0 rgba(0, 0, 0, 0.09);
  position: absolute;
  display: flex;
  align-items: center;
  padding-left: 15px;
  .circle {
    border-radius: 50%;
    height: 11px;
    width: 11px;
  }
  .data {
    font-size: 12px;
    color: #7e7e7e;
    margin-left: 10px;
  }
}

HeatMap.js

import React, { useState, useEffect, useCallback } from 'react';
import shortid from 'shortid';

import drawMap from './drawMap';
import styles from './HeatMap.module.scss';

const HeatMap = (props) => {
  const [id] = useState(`heatmap-${shortid.generate()}`);
  
  const handleDrawMap = useCallback(() => {
    drawMap(id, styles); //記得把styles傳進去
  }, [id]);

  useEffect(() => {
    handleDrawMap()
  }, [handleDrawMap]);

  return <div id={id} className={styles.container}></div>;
};

export default React.memo(HeatMap);

drawMap.js中先定義messageWrapper:

const messageWrapper = d3
    .select(`#${id}`)
    .append('div')
    .attr('class', styles.messageWrapper)
    .html(
      `<div class="${styles.circle}"></div><div class="${styles.data}"></div>`
    )
    .attr('style', 'display: none;');

監聽滑鼠事件改變位置與內容:

.on('mouseover', function(d) {
  d3.select(this)
    .attr('stroke', d3.rgb(colorScale(d.value)).darker(0.5))
    .attr('stroke-width', 2);

  d3.select(`.${styles.messageWrapper} .${styles.data}`).html(
    `${d.date} : ${d.value}`
  ); //資料內容
  d3.select(`.${styles.messageWrapper} .${styles.circle}`).attr(
    'style',
    `background-color: ${colorScale(d.value)}`
  ); //圓形的顏色與所選擇的方形相同

  messageWrapper.attr(
    'style',
    () =>
      `display: flex; left: ${(d.weekNum - 1) * 15 +
        (d.weekNum - 1) * 1 +
        50}px; top: ${d.day * 15 + d.day * 1}px`
  ); // 調整位置
})
.on('mouseleave', function() {
  d3.select(this).attr('stroke-width', '0');
  messageWrapper.attr('style', () => 'display: none;');
})


上一篇
Day22 實戰!Github Heat Map 產生器_熱力圖的進場動畫
下一篇
Day24 實戰!Github Heat Map 產生器_接上open api
系列文
資料視覺化!D3入門到實戰30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言