一、目標
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