一、目标
1、需求:项目中会用到纵向时间轴,且有大量数据,支持自动播放和翻页;
2、基于该目标去分析,发现时间轴组件确实不少,但综合考虑稳定性、可扩展性、文档API以及是否开源(涉及版权问题),最后选定综合评价较高的echarts 时间轴组件。
二、实现效果
三、步骤
1、在刚开始使用echarts时间轴做demo时,错误的选择了2.x稳定版本的时间轴组件,结果发现死活不支持纵轴(Y轴);
2、基于上述经验,索性选择最新版(4.2.1)进行加载,下载地址:https://echarts.baidu.com/download.html。在此提醒各位,鉴于文档一般不太好理解,建议先全量下载源码包,从里面的demo中找到相关示例,然后基于这个示例去改进,看是否能满足业务需求,如果不能,再去翻相关的API文档;在此源码包中,就有个test目录,test目录下有个timeline-layout.html文件,里面有各式各样的时间轴,步骤1中碰到的问题一下子就解决了;再比如,我需要时间轴和时间标签在页面的左侧显示,同时时间标签也要在轴的左侧显示,经过如下的两个样例,我只要稍微合并下参数就能达到效果了;
3、找到时间纵轴以后,发现时间轴是作为各种统计图的一个基础组件,需要把时间轴从各种图表中单独抽离出来。先是参考了一些前辈的经验(https://blog.csdn.net/wanhongyanwhy/article/details/41143371),发现只需要timeline参数即可,其它的配置参数全部可以去掉,但实际验证过程中发现没法直接点击时间轴上的坐标来做时间切换了,经过一番尝试,发现原来需要保留timeline的axisType和baseOption的trigger参数,完整代码见github;
4、需要监控时间轴当前选中时间的位置变化,只有这样才能知道是否要进入下一页或者上一页数据,在步骤3中引用的经验中发现刚好有个时间轴的change事件:
var ecConfig = require('echarts/config');
myChart.on(ecConfig.EVENT.TIMELINE_CHANGED, showTime);
function showTime(params){
console.log(params)
};
实践过程中发现:echarts 4.x没有ecConfig这个对象。灵机一动,原来我只需要关心TIMELINE_CHANGED值是什么即可,而且echarts 2.x版本不会报错,所以里面一定有对应的映射关系,结果下载了一个全量的2.x最新版本的echarts,果然就找到了:TIMELINE_CHANGED的值为:timelineChanged,问题一下子就迎刃而解;
5、能够监控到位置变化后,还需要根据位置变化,刷新时间轴上的数据变化(翻页);而时间轴主要的事件只有click和timelineChanged 2个事件,前者只能识别时间轴上的时间标签的点击事件(不包括下一个、上一个按钮的点击事件),后者可以识别时间轴上所有的位置变化,并标记出当前的位置,而翻页则至少需要2个位置对比(上一次的位置和当前位置);也就是说timelineChanged事件包含了click事件,所以最终我们只需要关心timelineChanged事件即可;
6、翻页发生时,需要变更当前位置所在的时间标签。比如翻下一页时,当前位置应该从最下的标签变更到最上面的标签,而系统默认还是下一页的最下面的标签,经过验证,发现设置echarts option中的timeline的currentIndex可以达成此目的;
7、在Vue项目中,需要引入echarts的js,按照本人经验,在下载页面,点击在线定制,仅需要勾选Timeline和最下面的工具集以及代码压缩这3个复选框,把生成的echarts.min.js放入项目的lib目录下;
8、核心代码如下所示,完整代码见github:
timeline.js代码:
import echarts from "../lib/echarts.min";
let _tlJson = {
timeline: {
axisType: 'category',
lineStyle: {
show: true
},
symbol: "circle",
itemStyle: {
normal: {
color: "rgba(194,53,49, 0.5)"
}
},
controlStyle: {
showPrevBtn: true,
showNextBtn: true,
normal: {
color: "rgba(194,53,49, 0.5)",
borderColor: "rgba(194,53,49, 0.5)"
}
},
orient: "vertical",
inverse: true,
x: null,
x2: 0,
y: 40,
width: 55,
height: "80%",
loop: false,
autoPlay: false,
playInterval: 1000
},
baseOption: {
tooltip: {
'trigger': 'axis'
},
}
};
export default class Timeline {
constructor() {
this.chart = undefined;
this.lastIndex = 0;
}
static makeTimeline(el, dataJson, callback) {
let tl = new Timeline();
let count = dataJson.timeline.data.length;
let option = echarts.util.merge(_tlJson, dataJson, true);
option = option || {};
tl.chart = echarts.init(el, null, {});
tl.chart.on("timelineChanged", (params) => {
let lastIndex = tl.lastIndex;
let curIndex = params.currentIndex;
console.log("last index:" + lastIndex + ",current index:" + curIndex);
if (count === 0) {
console.log("no data.");
return;
} else if (lastIndex != curIndex) {
tl.lastIndex = curIndex;
console.log("move " + lastIndex + " to " + curIndex);
callback.callback(tl, params);
} else if (lastIndex === curIndex) {
if (lastIndex === 0) {
callback.last(tl, params);
} else {
callback.next(tl, params);
}
}
});
tl.chart.setOption(option);
}
update(dataJson) {
let option = echarts.util.merge(_tlJson, dataJson, true);
let lastIndex = option.timeline.currentIndex;
this.chart.setOption(option);
if (lastIndex != undefined) {
this.lastIndex = lastIndex;
} else {
this.lastIndex = 0;
}
}
}
Timeline.vue代码:
<template>
<div id="timeline">
<div ref="timeline-table" class="timeline-table"></div>
</div>
</template>
<script>
import Timeline from "../commons/timeline";
export default {
name: "Timeline",
data() {
return {};
},
mounted: function() {
console.log("start to init timeline.");
let node = this.$refs["timeline-table"];
console.log("timeline=" + JSON.stringify(node));
let start = 2000;
let len = 10;
let self = this;
let dataJson = {
timeline: {
data: self.getDataJson(start, len, 0),
label: {
formatter: function(s) {
return new Date(s).getFullYear();
}
}
}
};
let limitPage = [1, 20];
let curPage = 10;
let callback = {
callback: (timeline, param) => {
let index = param.currentIndex;
console.log("curPage:" + curPage + ",curIndex:" + index);
},
last: (timeline, param) => {
let index = param.currentIndex;
if (curPage > limitPage[0]) {
curPage -= 1;
dataJson.timeline.data = self.getDataJson(start, len, -1);
let maxIndex = dataJson.timeline.data.length - 1;
dataJson.timeline.currentIndex = maxIndex;
timeline.update(dataJson);
start -= len;
}
console.log("-curPage:" + curPage + ",curIndex:" + index);
},
next: (timeline, param) => {
let index = param.currentIndex;
if (curPage < limitPage[1]) {
curPage += 1;
dataJson.timeline.data = self.getDataJson(start, len, 1);
dataJson.timeline.currentIndex = 0;
timeline.update(dataJson);
start += len;
}
console.log("+curPage:" + curPage + ",curIndex:" + index);
}
};
Timeline.makeTimeline(node, dataJson, callback);
},
methods: {
getDataJson: function(start, len, delta) {
let data = [];
for (let i = 0; i < len; i++) {
data[i] = start + i + delta * len + "-01-01";
}
console.log("current page:" + data);
return data;
}
}
};
</script>
<style scoped>
.timeline-table {
text-align: center;
margin: 20px;
width: 70px;
height: 400px;
background: #fff;
}
</style>
四、总结
1、echarts确实功能强大,且足够灵活,但是文档写得不清不楚,还好示例足够直观,一下子省了不少时间;
2、网上资料没有查到有设置timeline的currentIndex(默认时间轴的选中位置),试了几种方案,发现上述方案可行;
3、官方示例或者网上示例中,代码层次不够清晰,此文中把时间轴的基础数据单独抽成一个组件(timeline.js),而时间轴上可动态变化的数据则抽象到Timeline.vue页面,可以自由定制,当然也包括页面的翻页请求。
五、参考资料
[1]https://blog.csdn.net/wanhongyanwhy/article/details/41143371