上一篇: 盒须图

下一篇: 弦图

代码结构和初始化画布的Chart对象介绍,请先看 这里

本图完整的源码地址: 这里

1 图表效果

2 数据

{"2018-01-01": 2,"2018-01-02": 1,"2018-01-03": 2,"2018-01-04": 2,"2018-01-05": 4,"2018-01-06": 3,"2018-01-07": 6,"2018-01-08": 4,"2018-01-09": 1,"2018-01-10": 4,"2018-01-11": 3,"2018-01-12": 5,"2018-01-13": 0,"2018-01-14": 0,"2018-01-15": 0,"2018-01-16": 2,"2018-01-17": 1,"2018-01-18": 3,"2018-01-19": 6,"2018-01-20": 5,"2018-01-21": 1,"2018-01-22": 2,"2018-01-23": 1,"2018-01-24": 3,"2018-01-25": 2,"2018-01-26": 5,"2018-01-27": 4,"2018-01-28": 6,"2018-01-29": 2,"2018-01-30": 1,"2018-01-31": 3,"2018-02-01": 1,"2018-02-02": 0,"2018-02-03": 3,"2018-02-04": 6,"2018-02-05": 1,"2018-02-06": 1,"2018-02-07": 3,"2018-02-08": 3,"2018-02-09": 2,"2018-02-10": 5,"2018-02-11": 1,"2018-02-12": 0,"2018-02-13": 3,"2018-02-14": 2,"2018-02-15": 3,"2018-02-16": 4,"2018-02-17": 5,"2018-02-18": 6,"2018-02-19": 0,"2018-02-20": 1,"2018-02-21": 2,"2018-02-22": 1,"2018-02-23": 3,"2018-02-24": 5,"2018-02-25": 4,"2018-02-26": 2,"2018-02-27": 1,"2018-02-28": 2,"2018-03-01": 3,"2018-03-02": 2,"2018-03-03": 5,"2018-03-04": 6,"2018-03-05": 0,"2018-03-06": 1,"2018-03-07": 6,"2018-03-08": 1,"2018-03-09": 4,"2018-03-10": 5,"2018-03-11": 4,"2018-03-12": 2,"2018-03-13": 0,"2018-03-14": 2,"2018-03-15": 3,"2018-03-16": 2,"2018-03-17": 4,"2018-03-18": 3,"2018-03-19": 1,"2018-03-20": 3,"2018-03-21": 2,"2018-03-22": 4,"2018-03-23": 1,"2018-03-24": 4,"2018-03-25": 3,"2018-03-26": 6,"2018-03-27": 2,"2018-03-28": 2,"2018-03-29": 1,"2018-03-30": 3,"2018-03-31": 2,"2018-04-01": 3,"2018-04-02": 2,"2018-04-03": 5,"2018-04-04": 6,"2018-04-05": 0,"2018-04-06": 1,"2018-04-07": 6,"2018-04-08": 1,"2018-04-09": 4,"2018-04-10": 5,"2018-04-11": 4,"2018-04-12": 2,"2018-04-13": 0,"2018-04-14": 2,"2018-04-15": 3,"2018-04-16": 2,"2018-04-17": 4,"2018-04-18": 3,"2018-04-19": 1,"2018-04-20": 3,"2018-04-21": 2,"2018-04-22": 4,"2018-04-23": 1,"2018-04-24": 4,"2018-04-25": 3,"2018-04-26": 6,"2018-04-27": 2,"2018-04-28": 2,"2018-04-29": 1,"2018-04-30": 3,"2018-05-01": 2,"2018-05-02": 1,"2018-05-03": 2,"2018-05-04": 2,"2018-05-05": 4,"2018-05-06": 3,"2018-05-07": 6,"2018-05-08": 4,"2018-05-09": 1,"2018-05-10": 4,"2018-05-11": 3,"2018-05-12": 5,"2018-05-13": 0,"2018-05-14": 0,"2018-05-15": 0,"2018-05-16": 2,"2018-05-17": 1,"2018-05-18": 3,"2018-05-19": 6,"2018-05-20": 5,"2018-05-21": 1,"2018-05-22": 2,"2018-05-23": 1,"2018-05-24": 3,"2018-05-25": 2,"2018-05-26": 5,"2018-05-27": 4,"2018-05-28": 6,"2018-05-29": 2,"2018-05-30": 1,"2018-05-31": 3
}

3 关键代码

导入数据

d3.json('./data.json').then(function(data){....

设置一些样式配置参数,例如单元格大小、颜色等

const config = {margins: {top: 80, left: 50, bottom: 50, right: 50},textColor: 'black',title: '日期热力图',hoverColor: 'red',startTime: '2018-01-01',endTime: '2018-05-31',cellWidth: 20,cellHeight: 20,cellPadding: 1,cellColor1: 'white',cellColor2: 'green',lineColor: 'yellow',lineWidth: 2}

初始化一些常量

/* ----------------------------初始化常量------------------------  */const startTime = new Date(config.startTime);const endTime = new Date(config.endTime);const widthOffset = config.cellWidth + config.cellPadding;const heightOffset = config.cellHeight + config.cellPadding;

定义颜色插值转化函数

/* ----------------------------颜色转换------------------------  */chart.scaleColor = d3.scaleLinear().domain([0, d3.max(Object.values(data))]).range([config.cellColor1, config.cellColor2]);

渲染矩形,此处首先使用了一个while循环遍历startTimeendTime之间的所有日期,将未充填颜色的矩形绘制出来。随后再根据导入的data数据,填充对应的矩形的颜色

/* ----------------------------渲染矩形------------------------  */chart.renderRect = function(){let currentYear, currentMonth;let yearGroup, monthGroup;const initDay = startTime.getDay();let currentDay = initDay;const totalDays = getTotalDays(startTime, endTime) + initDay;const mainBody = chart.body().append('g').attr('class', 'date').attr('transform', 'translate(' + 35 + ',' + 50 + ')')while(currentDay <= totalDays){let currentDate = getDate(startTime, currentDay).split('-');if(!currentYear || currentDate[0] !== currentYear){currentYear = currentDate[0];yearGroup = mainBody.append('g').attr('class', 'year ' + currentYear);}if (!currentMonth || currentDate[1] !== currentMonth){currentMonth = currentDate[1];monthGroup = yearGroup.append('g').attr('class', 'month ' + currentMonth);}monthGroup.append('g').attr('class', 'g ' + currentDate.join('-')).datum(currentDate.join('-')).append('rect').attr('width', config.cellWidth).attr('height', config.cellHeight).attr('x', Math.floor(currentDay / 7) * widthOffset).attr('y', currentDay % 7 * heightOffset);currentDay++;}d3.selectAll('.g').each(function(d){d3.select(this).attr('fill', chart.scaleColor(data[d] || 0)).datum({time: d, value: data[d] || 0});});function getTotalDays(startTime, endTime){return Math.floor((endTime.getTime() - startTime.getTime()) / 86400000);}function getDate(startTime, day){const date =  new Date(startTime.getTime() + 86400000 * (day - initDay));return d3.timeFormat("%Y-%m-%d")(date);}}

接着渲染分割线,找到每月最后一天对应的矩形,然后根据其位置绘制path路径,这里有两种情况,一种是分割线有拐点,另一种为一条竖直的线。

/* ----------------------------渲染分隔线------------------------  */chart.renderLine = function(){const initDay = startTime.getDay();const days = [initDay-1];const linePaths = getLinePath();d3.select('.date').append('g').attr('class', 'lines').selectAll('path').data(linePaths).enter().append('path').attr('stroke', config.lineColor).attr('stroke-width', config.lineWidth).attr('fill', 'none').attr('d', (d) => d);function getLinePath(){const paths = [];d3.selectAll('.month').each(function(d,i){days[i+1] = days[i] + this.childNodes.length;});days.forEach((day,i) => {let path = 'M';let weekDay = day < 0 ? 6 : day % 7;if (weekDay !== 6) {path += Math.floor(day / 7) * widthOffset + ' ' + 7 * heightOffset;path +=  ' l' + '0' + ' ' + (weekDay - 6) * heightOffset;path += ' l' + widthOffset + ' ' + '0';path += ' l' + '0' + ' ' + (-weekDay - 1) * heightOffset;} else {path += (Math.floor(day / 7) + 1) * widthOffset + ' ' + 7 * heightOffset;path +=  ' l' + '0' + ' ' + (-7) * heightOffset;}paths.push(path);});return paths;}}

然后渲染文本标签,注意月份标签的间距约为4.25倍单元格大小(一周大约有4.25周)

/* ----------------------------渲染文本标签------------------------ */chart.renderText = function(){let week = ['Sun', 'Mon', 'Tue', 'Wed', 'Tur', 'Fri', 'Sat'];d3.select('.year').append('g').attr('class', 'week').selectAll('.label').data(week).enter().append('text').attr('class', 'label').attr('x', -40).attr('y', heightOffset/2).attr('dy', (d,i) => i * heightOffset + 4).text((d)=>d);let months = d3.timeMonth.range(new Date(startTime.getFullYear(), startTime.getMonth(), startTime.getDate()), new Date(endTime.getFullYear(), endTime.getMonth(), endTime.getDate()));months = months.map((d) => d3.timeFormat("%b")(d));d3.select('.year').append('g').attr('class', 'month-label').selectAll('text').data(months).enter().append('text').attr('x', (d,i) => i*widthOffset*4.25 + widthOffset*2).attr('y', -10).text((d) => d)}

最后绑定鼠标交互事件,悬停在矩形上时显示对应日期

/* ----------------------------绑定鼠标交互事件------------------------  */chart.addMouseOn = function(){//防抖函数function debounce(fn, time){let timeId = null;return function(){const context = this;const event = d3.event;timeId && clearTimeout(timeId)timeId = setTimeout(function(){d3.event = event;fn.apply(context, arguments);}, time);}}d3.selectAll('.g').on('mouseenter', function(d){const e = d3.event;const position = d3.mouse(chart.svg().node());d3.select(e.target).attr('fill', config.hoverColor);chart.svg().append('text').classed('tip', true).attr('x', position[0]+5).attr('y', position[1]).attr('fill', config.textColor).text(d.time);}).on('mouseleave', function(d){const e = d3.event;d3.select(e.target).attr('fill', chart.scaleColor(d.value));d3.select('.tip').remove();}).on('mousemove', debounce(function(){const position = d3.mouse(chart.svg().node());d3.select('.tip').attr('x', position[0]+5).attr('y', position[1]-5);}, 6));}

大功告成!!!


如果觉得这篇文章帮助了您,请打赏一个小红包鼓励作者继续创作哦!!!

D3 二维图表的绘制系列(二十七)日历热力图相关推荐

  1. D3 二维图表的绘制系列(一)介绍

    1 介绍 D3 (或者叫 D3.js )是一个基于 web 标准的 JavaScript 可视化库. D3 可以借助 SVG, Canvas 以及HTML 将你的数据生动的展现出来. D3 结合了强大 ...

  2. D3 二维图表的绘制系列(二十六)盒须图

    上一篇: 中国地图 下一篇: 日历热力图 代码结构和初始化画布的Chart对象介绍,请先看 这里 本图完整的源码地址:这里 1 图表效果 2 数据 {"data": [{" ...

  3. D3 二维图表的绘制系列(六)基础折线图

    上一篇: 横向柱状图 https://blog.csdn.net/zjw_python/article/details/98209333 下一篇: 堆叠面积图 https://blog.csdn.ne ...

  4. D3 二维图表的绘制系列(二十二)桑基图sankey

    上一篇: 仪表盘图 https://blog.csdn.net/zjw_python/article/details/98596174 下一篇: 旭日图 https://blog.csdn.net/z ...

  5. D3 二维图表的绘制系列(十五)雷达图

    上一篇: 气泡图 https://blog.csdn.net/zjw_python/article/details/98485368 下一篇: 矩形树状图 https://blog.csdn.net/ ...

  6. D3 二维图表的绘制系列(二十三)旭日图

    上一篇: 桑基图 https://blog.csdn.net/zjw_python/article/details/98611559 下一篇: 力导向图 https://blog.csdn.net/z ...

  7. D3 二维图表的绘制系列(八)曲线图

    上一篇: 堆叠面积图 https://blog.csdn.net/zjw_python/article/details/98214359 下一篇: 基础饼图 https://blog.csdn.net ...

  8. D3 二维图表的绘制系列(二十)河流图

    上一篇: 封闭图 https://blog.csdn.net/zjw_python/article/details/98591118 下一篇: 仪表盘图 https://blog.csdn.net/z ...

  9. D3 二维图表的绘制系列(十四)气泡图

    上一篇: 多符号散点图 https://blog.csdn.net/zjw_python/article/details/98483989 下一篇: 雷达图 https://blog.csdn.net ...

最新文章

  1. java 简单json和对象相互转换
  2. 文件共享服务器第二部,第二章-构建Samba文件共享服务器.docx
  3. Flutter 实现微信摇一摇的功能 Flutter 加速度感应
  4. 当 Messaging 遇上 Jepsen
  5. 2015 NI 校招笔试机试面试
  6. 网上购物系统的设计与实现
  7. PHP正则淘口令,Flutter代码锦囊---淘口令复制弹窗
  8. 开源dns软件PowerDNS BIND9 mydns
  9. 利用 QTcpSocket 实现的进程间通信
  10. android time计时器,android 计时器的三种实现(Chronometer、Timer、handler)
  11. NGINX安装及操作笔记
  12. NTL密码算法开源库-大整数ZZ类(一)
  13. 鸿蒙os跑分,预装鸿蒙OS 2.0!华为MatePad Pro2跑分曝光:麒麟9000、8GB内存
  14. 软件测试前置基础知识(基本概念,DOS命令)
  15. linux内核函数出错的返回值
  16. VLAN(TRUNK端口 ACCESS 端口)
  17. 蓄电池测量原理——TFN DC36060A 高精度充电机 蓄电池放电容量测试仪
  18. [易学易懂系列|golang语言|零基础|快速入门|(二)]
  19. 你不知道的CS模式的进程管理工具,状态监测、项目启停一目了然!
  20. 推荐一款可以工作汇报远程办公的电脑监控软件

热门文章

  1. python—实现str转MD5
  2. 搜索引擎优化排名的简单介绍
  3. Borg/Mesos/Yarn三大主流资源管理与调度系统对比
  4. 《坐天下很累》 张宏杰 (值得多看)
  5. python(函数)
  6. Java基于PHP+MySQL干洗店管理系统的设计与实现
  7. Android的Usb设备的监听(Dev)外设端口的判定以及耳机的插拔
  8. ElasticSearch-解决unassigned shareds问题
  9. gdb 笔记(11)— info 和 thread 命令(查看线程、切换线程)
  10. cmd 运行不要显示黑色窗口