Echarts 功能十分强大,可以实现多种图表效果,下面简单介绍下最近使用 Eharts 实现的一个项目进度甘特图。
下面是实现的效果:
目录
一.概览 Echarts 基本内容
1.官网文档
首先基本的前端搭建必须的,然后应该大致浏览一下 Echarts 官网 文档 专题下的 教程 和 配置项手册,如果不是要实现特别复杂的效果和功能,这些内容基本就足够了。
2.查看示例
只看文字内容比较抽象,因为要做甘特图,可以先去 实例 中找一个甘特图研究下,看看别人是怎么实现的。你会发现Echarts官网好像只能在 实例 下找到官方示例图,别急,操作一番就可以看其他无私的贡献者发布的图表了。
点击官网页面右上角 “EN”,切换为英文版,
在 Get Started 下 点击 Gallery,
这样就能看到其他开发者贡献的 Demo 了,
点击 展开筛选,筛选你要找的图表类型。
下面这个图表(点击这里查看该图表Demo)和我们要实现的效果还是有几分类似的:
当然细节还得自己研究修改一下。
二.需求分析
从文章开头的图表效果来看,横坐标刻度值为时间,纵坐标为 “方案”、“纲要”、“成果” 三个项目阶段类目,每个阶段类目中包含按时完成(蓝色柱状图)和超时完成(红色柱状图)两种系列的数据,另外还有垂直于 x 时间坐标轴的 “计划开始时间”和“有效期”两条时间标线。结合查阅官网文档和demo,应该有以下初步结论:
1.数据系列(series)
每个阶段类目中实际应该有三种系列(series)的数据的,即应该有 开始时间、按时完成时间和超时完成时间三种系列,且三种系列的柱状图是堆叠在一起的。
2.标线 (markLine)
“计划开始时间” markLine 可以放在在 开始时间series 中,“有效期”markLine可以在 按时完成 或 超时完成 series 中。
3.数据对象结构
这里比较容易理解的传值结构,应该是传入 三个时间段 和 两个刻度值,类似下面这种结构:
/**
* 存储阶段进度的对象
*/
var stageProgress = {
// 方案 实际时间段
fangAnTimeBucket: ['2017-01-01', '2017-03-01'],
// 纲要 实际时间段
gangYaoTimeBucket: ['2017-02-26', '2017-08-29'],
// 成果 实际时间段
chengGuoTimeBucket: ['2017-08-29', '2017-12-06'],
// 计划开始时间
scheduledStartTime: '2016-12-22',
// 有效截止日期
validTime: '2017-05-10'
};
4.“超时”、“按时”划分
这里要求三个阶段实际时间段的首尾可以不连续,即三个阶段之间没有影响,所以,是否超时就取决于 有效期 与 阶段实际时间段 之间的关系了。比如,有效期在阶段实际时间段开始和结束时间之间,那么该阶段是超时的,但是该阶段的柱状图应该是蓝色红色共存的;有效期在阶段实际时间段结束时间之后,那么该阶段按时完成,应该都是蓝色的;有效期在阶段实际时间段开始时间之前,那么该阶段超时完成,应该都是红色的。
其实主要是第四点需要计算一下,其他 参考一下 配置项手册基本没有太大问题。
三.引入并配置 Echarts
1.坑
用新不用旧,但是经过我的测试,Echarets4.x 透明堆叠不起效果,即设置了 series 为 bar ,且 开始时间、按时完成时间、按时完成时间 三个 series 设置了 相同的 stack 后,开始时间 柱状图并不能起到遮盖其他系列柱状图的效果,于是尝试使用3.x 版本,是可以起到柱条堆叠辅助隐藏的效果的。(请同学们注意!!!评论区的seuedu同学指出,在4.x版本中,series里配置z属性可以覆盖,但本人未进行验证,请大家自行验证下!!!)
若果需要兼容 IE8 的话,那么你需要定制下载 Echarts(我在实际测试中发现,这个定制下载即使选中 兼容IE8 ,下载下来的js仍让无法兼容 IE8,真的是让人脑壳痛)。
2.具体操作
在你的页面中,放置一个 div,作为放置图表的容器,需要为它指定宽高。
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="content"></div>
控制图表的 js 代码基本结构如下:
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data:['销量']
},
xAxis: {
data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
下面是实现本文开头图表效果的 js,方法说明和需要注意的点基本都写在注释里了:
/**
* 存储阶段进度时间段的对象
*/
var stageProgress = {
// // 方案 实际时间段
// fangAnTimeBucket: ['2017-01-01', '2017-03-01'],
// // 纲要 实际时间段
// gangYaoTimeBucket: ['2017-02-26', '2017-08-29'],
// // 成果 实际时间段
// chengGuoTimeBucket: ['2017-08-29', '2017-12-06'],
// // 计划开始时间
// scheduledStartTime: '2016-12-22',
// // 有效截止日期
// validTime: '2017-05-10'
// // 方案 实际时间段
// fangAnTimeBucket: ['2017-01-01', '2017-01-20'],
// // 纲要 实际时间段
// gangYaoTimeBucket: ['2017-02-07', '2017-02-28'],
// // 成果 实际时间段
// chengGuoTimeBucket: ['2017-02-15', '2017-03-20'],
// // 计划开始时间
// scheduledStartTime: '2017-01-01',
// // 有效截止日期
// validTime: '2017-02-20'
// 方案 实际时间段
fangAnTimeBucket: ['2017-11-23', '2017-11-28'],
// 纲要 实际时间段
gangYaoTimeBucket: ['2017-11-25', '2017-11-30'],
// 成果 实际时间段
chengGuoTimeBucket: ['2017-12-06', '2017-12-12'],
// 计划开始时间
scheduledStartTime: '2017-11-23',
// 有效截止日期
validTime: '2017-12-02'
};
/**
* 横坐标轴时间刻度可选值
* 这里 month和year 没有考虑平闰年之分
*/
var timeInterval = {
day: 3600 * 1000 * 24,
month: 3600 * 1000 * 24 * 31,
year: 3600 * 1000 * 24 * 31 * 12,
};
/**
* 时间坐标轴标签单位应该精确到哪一位
*/
var xAxisLabelUnit = {
year: false,
month: false,
day: false
}
/**
* 获取合适的横坐标时间刻度间隔
*/
function getProperTimeAxisInterval() {
xAxisLabelUnit.year = false;
xAxisLabelUnit.month = false;
xAxisLabelUnit.day = false;
var timeDataArray = getXAxisData();
var begin = getTimeMilliseconds(timeDataArray[timeDataArray.length - 1]);
console.log("begin " + begin);
var periodMillis = getTimeMilliseconds(timeDataArray[timeDataArray.length - 1]) - getTimeMilliseconds(timeDataArray[0]);
console.log("periodMillis " + periodMillis);
var years = periodMillis / timeInterval.year;
console.log("years " + years);
var months = periodMillis / timeInterval.month;
console.log("months " + months);
var days = periodMillis / timeInterval.day;
console.log("days " + days);
if (months <= 1) {
xAxisLabelUnit.day = true;
return timeInterval.day * 2;
} else if (months <= 16) {
xAxisLabelUnit.month = true;
return timeInterval.month;
} else i