如何使用echarts做类似github日历贡献图?
在某个大神的博客中看到这个动态日历的组件,以类似github日历贡献图的方式展现了近段时间的文章数量。感觉非常棒,于是就产生了在jpress上做一个这种动态日历的想法,使用echarts热力图,最终实现效果如下图:
该组件前端基于echarts,后端基于jpress。
图表显示最近365天每天的文章发布数量,并以绿色展示当天的发布量,颜色越深则数量越大。 我们先将图表效果做出来再与后端的数据做结合。 首先我们需要先在页面中引入echarts.js,并定义相关的颜色:
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@5.2.1/dist/echarts.min.js"></script>
<script type="text/javascript">
var face ="light";
var color1, color2, color3, color4;
if("light" === face){
color1 = ["#ebedf0", "#c6e48b", "#7bc96f", "#239a3b", "#196127"];
color2 = "#ffffff";
color3 = "#3C4858";
color4 = "#f9f9f9";
}else{
color1 = ["#ebedf0", "#c6e48b", "#7bc96f", "#239a3b", "#196127"];
color2 = "#000";
color3 = "#fff";
color4 = "#212121";
}
</script>
然后在页面中用一个div做图表的容器:
<div id="japressCalendarHeatmap" style="width: 100% ; height: 150px"></div>
接下来编写相关的js渲染图表:
<script type="text/javascript">
function showJapressCalendarHeatmap(){
var mydata = [];
mydata.push( ['2020-10-25',0], ['2020-10-26',0], ['2020-10-27',0], ['2020-10-28',0], ['2020-10-29',0], ['2020-10-30',0], ['2020-10-31',0], ['2020-11-01',0], ['2020-11-02',0], ['2020-11-03',0], ['2020-11-04',0], ['2020-11-05',0], ['2020-11-06',0], ['2020-11-07',0], ['2020-11-08',0], ['2020-11-09',0], ['2020-11-10',0], ['2020-11-11',0], ['2020-11-12',0], ['2020-11-13',0], ['2020-11-14',0], ['2020-11-15',0], ['2020-11-16',0], ['2020-11-17',0], ['2020-11-18',0], ['2020-11-19',0], ['2020-11-20',0], ['2020-11-21',0], ['2020-11-22',0], ['2020-11-23',0], ['2020-11-24',0], ['2020-11-25',0], ['2020-11-26',0], ['2020-11-27',0], ['2020-11-28',0], ['2020-11-29',0], ['2020-11-30',0], ['2020-12-01',0], ['2020-12-02',0], ['2020-12-03',0], ['2020-12-04',0], ['2020-12-05',0], ['2020-12-06',0], ['2020-12-07',0], ['2020-12-08',0], ['2020-12-09',0], ['2020-12-10',0], ['2020-12-11',0], ['2020-12-12',0], ['2020-12-13',0], ['2020-12-14',0], ['2020-12-15',0], ['2020-12-16',0], ['2020-12-17',0], ['2020-12-18',0], ['2020-12-19',0], ['2020-12-20',1], ['2020-12-21',0], ['2020-12-22',0], ['2020-12-23',0], ['2020-12-24',0], ['2020-12-25',2], ['2020-12-26',0], ['2020-12-27',0], ['2020-12-28',0], ['2020-12-29',0], ['2020-12-30',0], ['2020-12-31',0], ['2021-01-01',0], ['2021-01-02',0], ['2021-01-03',0], ['2021-01-04',0], ['2021-01-05',0], ['2021-01-06',0], ['2021-01-07',0], ['2021-01-08',0], ['2021-01-09',0], ['2021-01-10',0], ['2021-01-11',0], ['2021-01-12',0], ['2021-01-13',0], ['2021-01-14',0], ['2021-01-15',0], ['2021-01-16',0], ['2021-01-17',0], ['2021-01-18',0], ['2021-01-19',0], ['2021-01-20',0], ['2021-01-21',0], ['2021-01-22',0], ['2021-01-23',0], ['2021-01-24',0], ['2021-01-25',0], ['2021-01-26',0], ['2021-01-27',0], ['2021-01-28',0], ['2021-01-29',0], ['2021-01-30',0], ['2021-01-31',0], ['2021-02-01',0], ['2021-02-02',0], ['2021-02-03',0], ['2021-02-04',0], ['2021-02-05',0], ['2021-02-06',0], ['2021-02-07',0], ['2021-02-08',0], ['2021-02-09',0], ['2021-02-10',0], ['2021-02-11',0], ['2021-02-12',0], ['2021-02-13',0], ['2021-02-14',0], ['2021-02-15',0], ['2021-02-16',0], ['2021-02-17',0], ['2021-02-18',0], ['2021-02-19',0], ['2021-02-20',0], ['2021-02-21',0], ['2021-02-22',0], ['2021-02-23',0], ['2021-02-24',0], ['2021-02-25',0], ['2021-02-26',0], ['2021-02-27',0], ['2021-02-28',0], ['2021-03-01',0], ['2021-03-02',0], ['2021-03-03',0], ['2021-03-04',0], ['2021-03-05',0], ['2021-03-06',0], ['2021-03-07',0], ['2021-03-08',0], ['2021-03-09',0], ['2021-03-10',0], ['2021-03-11',0], ['2021-03-12',0], ['2021-03-13',0], ['2021-03-14',0], ['2021-03-15',0], ['2021-03-16',0], ['2021-03-17',0], ['2021-03-18',0], ['2021-03-19',0], ['2021-03-20',0], ['2021-03-21',0], ['2021-03-22',0], ['2021-03-23',0], ['2021-03-24',0], ['2021-03-25',0], ['2021-03-26',0], ['2021-03-27',0], ['2021-03-28',0], ['2021-03-29',0], ['2021-03-30',0], ['2021-03-31',0], ['2021-04-01',0], ['2021-04-02',0], ['2021-04-03',0], ['2021-04-04',0], ['2021-04-05',0], ['2021-04-06',0], ['2021-04-07',0], ['2021-04-08',0], ['2021-04-09',0], ['2021-04-10',0], ['2021-04-11',0], ['2021-04-12',0], ['2021-04-13',0], ['2021-04-14',0], ['2021-04-15',0], ['2021-04-16',0], ['2021-04-17',0], ['2021-04-18',0], ['2021-04-19',0], ['2021-04-20',0], ['2021-04-21',0], ['2021-04-22',0], ['2021-04-23',0], ['2021-04-24',0], ['2021-04-25',1], ['2021-04-26',0], ['2021-04-27',0], ['2021-04-28',0], ['2021-04-29',0], ['2021-04-30',0], ['2021-05-01',0], ['2021-05-02',0], ['2021-05-03',0], ['2021-05-04',0], ['2021-05-05',0], ['2021-05-06',0], ['2021-05-07',0], ['2021-05-08',0], ['2021-05-09',0], ['2021-05-10',0], ['2021-05-11',0], ['2021-05-12',0], ['2021-05-13',0], ['2021-05-14',0], ['2021-05-15',0], ['2021-05-16',0], ['2021-05-17',0], ['2021-05-18',0], ['2021-05-19',0], ['2021-05-20',0], ['2021-05-21',0], ['2021-05-22',0], ['2021-05-23',0], ['2021-05-24',0], ['2021-05-25',0], ['2021-05-26',0], ['2021-05-27',0], ['2021-05-28',0], ['2021-05-29',0], ['2021-05-30',0], ['2021-05-31',0], ['2021-06-01',1], ['2021-06-02',0], ['2021-06-03',0], ['2021-06-04',0], ['2021-06-05',0], ['2021-06-06',0], ['2021-06-07',0], ['2021-06-08',0], ['2021-06-09',0], ['2021-06-10',0], ['2021-06-11',0], ['2021-06-12',0], ['2021-06-13',0], ['2021-06-14',0], ['2021-06-15',0], ['2021-06-16',0], ['2021-06-17',0], ['2021-06-18',0], ['2021-06-19',0], ['2021-06-20',0], ['2021-06-21',0], ['2021-06-22',0], ['2021-06-23',0], ['2021-06-24',0], ['2021-06-25',0], ['2021-06-26',0], ['2021-06-27',0], ['2021-06-28',0], ['2021-06-29',0], ['2021-06-30',0], ['2021-07-01',0], ['2021-07-02',0], ['2021-07-03',0], ['2021-07-04',0], ['2021-07-05',0], ['2021-07-06',0], ['2021-07-07',0], ['2021-07-08',0], ['2021-07-09',0], ['2021-07-10',0], ['2021-07-11',0], ['2021-07-12',0], ['2021-07-13',0], ['2021-07-14',0], ['2021-07-15',0], ['2021-07-16',0], ['2021-07-17',0], ['2021-07-18',0], ['2021-07-19',0], ['2021-07-20',0], ['2021-07-21',0], ['2021-07-22',0], ['2021-07-23',0], ['2021-07-24',0], ['2021-07-25',0], ['2021-07-26',0], ['2021-07-27',0], ['2021-07-28',0], ['2021-07-29',0], ['2021-07-30',0], ['2021-07-31',0], ['2021-08-01',0], ['2021-08-02',0], ['2021-08-03',0], ['2021-08-04',0], ['2021-08-05',0], ['2021-08-06',0], ['2021-08-07',0], ['2021-08-08',0], ['2021-08-09',0], ['2021-08-10',0], ['2021-08-11',0], ['2021-08-12',0], ['2021-08-13',0], ['2021-08-14',0], ['2021-08-15',0], ['2021-08-16',0], ['2021-08-17',0], ['2021-08-18',0], ['2021-08-19',0], ['2021-08-20',0], ['2021-08-21',0], ['2021-08-22',0], ['2021-08-23',0], ['2021-08-24',0], ['2021-08-25',0], ['2021-08-26',0], ['2021-08-27',0], ['2021-08-28',0], ['2021-08-29',0], ['2021-08-30',0], ['2021-08-31',0], ['2021-09-01',1], ['2021-09-02',0], ['2021-09-03',0], ['2021-09-04',0], ['2021-09-05',0], ['2021-09-06',0], ['2021-09-07',0], ['2021-09-08',0], ['2021-09-09',0], ['2021-09-10',0], ['2021-09-11',0], ['2021-09-12',0], ['2021-09-13',0], ['2021-09-14',0], ['2021-09-15',0], ['2021-09-16',0], ['2021-09-17',0], ['2021-09-18',0], ['2021-09-19',0], ['2021-09-20',0], ['2021-09-21',0], ['2021-09-22',0], ['2021-09-23',0], ['2021-09-24',0], ['2021-09-25',0], ['2021-09-26',0], ['2021-09-27',0], ['2021-09-28',0], ['2021-09-29',0], ['2021-09-30',0], ['2021-10-01',0], ['2021-10-02',0], ['2021-10-03',0], ['2021-10-04',0], ['2021-10-05',0], ['2021-10-06',0], ['2021-10-07',0], ['2021-10-08',0], ['2021-10-09',0], ['2021-10-10',0], ['2021-10-11',0], ['2021-10-12',0], ['2021-10-13',0], ['2021-10-14',0], ['2021-10-15',0], ['2021-10-16',5], ['2021-10-17',0], ['2021-10-18',1], ['2021-10-19',0], ['2021-10-20',0], ['2021-10-21',0], ['2021-10-22',0], ['2021-10-23',0], ['2021-10-24',0], ['2021-10-25',1], );
var chartDom = document.getElementById('japressCalendarHeatmap');
var myChart = echarts.init(chartDom);
var option = {
title: {
show:false,
top: 30,
},
tooltip: {
trigger: 'item',
formatter: function (params) {
return params.data[0] + '<br>数量:' + params.data[1];
}
},
visualMap: {
min: 0,
max: 5,
show: false,
showLabel: true,
calculable: false,
inRange: {
symbol: 'rect',
color: color1
},
itemWidth: 12,
itemHeight: 50,
orient: 'horizontal',
left: 'center',
top: 0
},
calendar: {
left: 'center',
top:'center',
name: {
textStyle: {
color: color3
}
},
cellSize: [13, 13],
splitLine: {
show: false
},
range: ['2020-10-25','2021-10-25'],
itemStyle: {
borderColor: color2,
borderWidth: 2
},
yearLabel: { show: false },
dayLabel: {
nameMap: 'cn', // 设置中文显示
color: color3,
fontSize:11,
},
monthLabel: {
nameMap: 'cn', // 设置中文显示
fontSize:11,
color: color3,
}
},
series: {
type: 'heatmap',
coordinateSystem: 'calendar',
data: mydata
}
};
option && myChart.setOption(option);
}
showJapressCalendarHeatmap();
</script>
到这里,静态的图表就完成了。 接下来我们需要调用实际的数据,这里我们编写一个jpress插件,来获取数据,下面是关键代码: ChartsArticleProvider.java:
package com.miigua.japress.charts.provider;
import com.miigua.japress.charts.service.ChartsArticleService;
import io.jboot.aop.annotation.Bean;
import io.jboot.components.cache.CacheTime;
import io.jboot.components.cache.annotation.Cacheable;
import io.jboot.service.JbootServiceBase;
import io.jpress.module.article.model.Article;
import java.util.List;
@Bean
public class ChartsArticleProvider extends JbootServiceBase<Article> implements ChartsArticleService {
@Override
@Cacheable(name = "japress-charts", key = "daylist-#(begin)-#(end)", liveSeconds = 30 * CacheTime.MINUTE, nullCacheEnable = true)
public List<Article> getDayListCount(String begin , String end){
begin += " 00:00:00";
end += " 23:59:59";
List<Article> articles = DAO.find("select *,DATE_FORMAT(created,'%Y-%m-%d') as days ,count(id) as counts , created from article GROUP BY days HAVING created >= ? and created <= ? order by created asc" , begin ,end);
return articles;
}
}
Util.java:
package com.miigua.japress.charts.utils;
import io.jboot.utils.DateUtil;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
public class Util {
/**
* 获取某个时间段内所有日期
* @param begin
* @param end
* @return
*/
public static List<String> getDayBetweenDates(String begin, String end) {
Date dBegin = DateUtil.parseDate(begin,DateUtil.datePattern);
Date dEnd = DateUtil.parseDate(end,DateUtil.datePattern);
List<String> lDate = new ArrayList<String>();
lDate.add(begin);
Calendar calBegin = Calendar.getInstance();
// 使用给定的 Date 设置此 Calendar 的时间
calBegin.setTime(dBegin);
Calendar calEnd = Calendar.getInstance();
// 使用给定的 Date 设置此 Calendar 的时间
calEnd.setTime(dEnd);
// 测试此日期是否在指定日期之后
while (dEnd.after(calBegin.getTime())) {
// 根据日历的规则,为给定的日历字段添加或减去指定的时间量
calBegin.add(Calendar.DAY_OF_MONTH, 1);
lDate.add(DateUtil.toString(calBegin.getTime(),DateUtil.datePattern));
}
return lDate;
}
}
CalendarHeatmapDirective.java:
package com.miigua.japress.charts.directive;
import com.jfinal.aop.Inject;
import com.jfinal.template.Env;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Scope;
import com.miigua.japress.charts.service.ChartsArticleService;
import com.miigua.japress.charts.utils.Util;
import io.jboot.utils.DateUtil;
import io.jboot.web.directive.annotation.JFinalDirective;
import io.jboot.web.directive.base.JbootDirectiveBase;
import io.jpress.module.article.model.Article;
import java.util.*;
@JFinalDirective("japressCalendarHeatmap")
public class CalendarHeatmapDirective extends JbootDirectiveBase {
@Inject
private ChartsArticleService articleProvider;
@Override
public void onRender(Env env, Scope scope, Writer writer) {
//需要展示的天数
int days = getParaToInt("days", scope, 365);
Date now = new Date();
//计算起始时间
String endTime = DateUtil.toString(now,DateUtil.datePattern);
Date temp = DateUtil.addDays(now , -days);
String startTime = DateUtil.toString(temp,DateUtil.datePattern);
//获取起始时间内每天的数量
List<Article> dayArticleCount = articleProvider.getDayListCount(startTime, endTime);
//获取起始时间内的每一天
List<String> dateList = Util.getDayBetweenDates(startTime, endTime);
Map<String, Long> dateMap = new HashMap<>();
long max = 0;
for(Article a : dayArticleCount) {
String day = a.get("days");
long count = a.get("counts");
if(count > max){
max = count;
}
dateMap.put(day, count);
}
List<Map<String,Object>> resultList = new ArrayList<>();
for(String day : dateList) {
long count = 0;
if(dateMap.get(day) != null) {
count = dateMap.get(day);
}
Map<String,Object> map = new HashMap<>(2);
map.put("day",day);
map.put("count",count);
resultList.add(map);
}
Map<String,Object> resultMap = new HashMap<>(2);
resultMap.put("dayList",resultList);
resultMap.put("max",max);
resultMap.put("min",0);
scope.setLocal("japressCalendarHeatmap", resultMap);
renderBody(env, scope, writer);
}
@Override
public boolean hasEnd() {
return true;
}
}
以上便是插件核心代码。 接下来我们开始在html页面中读取后端的数据,为了调用方便以及页面模板的整洁,我们将图表html和js相关的代码单独定义到一个文件,使用的时候直接引入调用。 这里我的文件名是 japress-charts.html,文件内容如下:
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@5.2.1/dist/echarts.min.js"></script>
<script type="text/javascript">
var face ="light";
var color1, color2, color3, color4;
if("light" === face){
color1 = ["#ebedf0", "#c6e48b", "#7bc96f", "#239a3b", "#196127"];
color2 = "#ffffff";
color3 = "#3C4858";
color4 = "#f9f9f9";
}else{
color1 = ["#ebedf0", "#c6e48b", "#7bc96f", "#239a3b", "#196127"];
color2 = "#000";
color3 = "#fff";
color4 = "#212121";
}
</script>
#define japressCalendarHeatmap(width,height,dayCount)
#japressCalendarHeatmap(days=dayCount)
#if(japressCalendarHeatmap.dayList.size() > 0)
<div id="japressCalendarHeatmap" style="width: #(width ?? '100%') ; height: #(height ?? '185px')"></div>
<script type="text/javascript">
function showJapressCalendarHeatmap(){
var mydata = [];
mydata.push(#for(j:japressCalendarHeatmap.dayList) ['#(j.day)',#(j.count)], #end);
var chartDom = document.getElementById('japressCalendarHeatmap');
var myChart = echarts.init(chartDom);
var option = {
title: {
show:false,
top: 30,
},
tooltip: {
trigger: 'item',
formatter: function (params) {
return params.data[0] + '<br>数量:' + params.data[1];
}
},
visualMap: {
min: #(japressCalendarHeatmap.min),
max: #(japressCalendarHeatmap.max),
show: false,
showLabel: true,
calculable: false,
inRange: {
symbol: 'rect',
color: color1
},
itemWidth: 12,
itemHeight: 50,
orient: 'horizontal',
left: 'center',
top: 0
},
calendar: {
left: 'center',
top:'center',
name: {
textStyle: {
color: color3
}
},
cellSize: [13, 13],
splitLine: {
show: false
},
range: ['#(japressCalendarHeatmap.dayList.get(0).day)','#(japressCalendarHeatmap.dayList.get(japressCalendarHeatmap.dayList.size()-1).day)'],
itemStyle: {
borderColor: color2,
borderWidth: 2
},
yearLabel: { show: false },
dayLabel: {
nameMap: 'cn', // 设置中文显示
color: color3,
fontSize:11,
},
monthLabel: {
nameMap: 'cn', // 设置中文显示
fontSize:11,
color: color3,
}
},
series: {
type: 'heatmap',
coordinateSystem: 'calendar',
data: mydata
}
};
option && myChart.setOption(option);
}
showJapressCalendarHeatmap();
</script>
#end
#end
#end
在需要使用该图表的时候我们只需要如下方式引入调用就可以了:
#include("./japress-charts.html")
#@japressCalendarHeatmap('100%','150px',365)
调用时支持3个自定义的参数:组件宽,组件高,要展示的天数。 ok,大功告成!注意再使用的时候要先安装刚才编写的插件。
版权声明:
作者:Miigua
链接:https://www.miigua.com/article/282.html
来源:米瓜的博客
文章版权归作者所有,未经允许请勿转载。
Gudrun
PatrickMot
Logan
Astetframp
Ashton