百度echart柱图、折线图、饼图、Map类型等类似视图的动态加载数据

这几天接触的一个项目需要使用到百度的echart,所以简单查找并实验了几款比较简单且适用性较广的例子,做了几个简单的Demo,一来是对echart有所了解,二来也是记录一下,方便以后进一步的优化。

首先,是echart的js文件,一般情况下,这一个文件基本就可以了,但是由于本文还需要涉及到地图类型的处理,所以还需要一个地图包,,这个echart官网已经下架了,说是因为部分矢量数据不正确,建议用百度地图,但是在相关的实例中还是可以找到的,也挺好找的,就先拿这个练手,为了不暴露,代码中涉及到的url都用urlurlrul替代。

然后就是把相关的包导入项目,并且做引入

其实对于每一个实例而言,其过程都是类似的,先初始化相关参数,然后通过ajax动态加载需要更新的数据,

比如第一个柱图,首先初始化

 option = {
        title: {      //标题组件
            text: '消费金额统计图',
            x: 'center',
            y: 'top',
            textAlign: 'left'
        },
        tooltip: {    //提示框组件
            trigger: 'axis',
            showDelay: 5
        },
        legend: {     //图例组件
            left: 'left',
            data: ['消费金额(元)', '周环比(100%)']
        },
        grid: {       //直角坐标系内绘图网格
            x: 40,
            x2: 100,
            y2: 150,
            containLabel: true
        },
        toolbox: {     //工具栏
            feature: {
                magicType: {
                    type: ['line', 'bar', 'tiled']
                },
                dataView: {readOnly: false},
                saveAsImage: {}
            }
        },
        xAxis: {       //直角坐标系 grid 中的 x 轴
            type: 'category',
            boundaryGap: true,
            axisLabel: {
                interval: 0,
                rotate: 40,
            },
            textStyle: '#333',
            name: "时间",
            axisTick: {
                show: false
            },
            data: []
        },
        yAxis: {       //直角坐标系 grid 中的 y 轴
//         	min: 0,
//         	max: 4000,
//         	interval:500,
            name: "值",
            type: 'value'
        },
        series: [      //系列列表
            {
                name: '消费金额(元)',
                type: 'line',
                stack: '总量',
                itemStyle: {normal: {label: {show: true}}},
                data: [133, 21, 321, 2]
            },
            {
                name: '周环比(100%)',
                type: 'line',
                itemStyle: {normal: {label: {show: true}}},
                stack: '总量',
                data: [12, 32, 11, 45]
            }
        ]
    };
    myChart.setOption(option);  

然后ajax动态加载数据:

function getChartData() {
        //获得图表的options对象
        var options = myChart.getOption();
        //通过Ajax获取数据
        $.ajax({
            type: "post",
            async: false, //同步执行
            url: "urlurlrul/getEchartData",
            data: {},
            dataType: "json", //返回数据形式为json
            success: function (result) {
                if (result) {
                    options.title[0].text=result.title;
                    options.legend[0].data = result.legend;
                    options.xAxis[0].data = result.category;
                    for (var i = 0; i < result.series.length; i++) {
                        options.series[i].name = result.series[i].name;
                        options.series[i].type = result.series[i].type;
                        options.series[i].data = result.series[i].data;
                    }
                    myChart.hideLoading();
                    myChart.setOption(options);
                }
            },
            error: function (errorMsg) {
                alert("图表请求数据失败!");
                myChart.hideLoading();
            }
        });
    }

其实这部分最关键,也是整个项目最关键的部分,就是ajax动态加载之后的数据接收处理,由于都是自己的练手代码,所以我都在后台把数据处理好再发送。后端主要有三个主要的方法,EchartData(),Series(),和echartGetData()。EchartData()用于处理图表Option的主要参数,Series()处理series中data和其他相关参数,echartGetData()则是用于处理数据和传输数据的方法。

	@PostMapping(value = "/getEchartData",produces = "application/json;charset=utf-8")
	public EchartData echartGetData() {
		String title = "折线图和柱图";
		
		List<Object> seriesList = new ArrayList<>();//动态数据集
		
		List<String> legend = new ArrayList<>();//图例
		
		legend.add("销量");
		legend.add("售价");

		
		List<String> category = new ArrayList<>();//坐标轴
		category.add("一月份");
		category.add("二月份");
		category.add("三月份");
		category.add("四月份");
		
		List<Integer> xList = new ArrayList<>();
		xList.add(11);
		xList.add(24);
		xList.add(31);
		xList.add(14);
		seriesList.add(new Series("销量","line",xList));

		List<Integer> xList2 = new ArrayList<>();
		xList2.add(31);
		xList2.add(44);
		xList2.add(51);
		xList2.add(36);
		
		seriesList.add(new Series("售价","bar",xList2));
		
		EchartData echartData = new EchartData(title,category,legend,seriesList);
		return echartData;
	
		
	}



public class EchartData {
	
	public String title;
	public List<String> category = new ArrayList<>();
	public List<String> legend = new ArrayList<>();
	public List<Object> series = new ArrayList<>();
	public EchartData(String title,List<String> categoryList,List<String> legendList,List<Object> seriesList) {
		// TODO Auto-generated constructor stub
		this.title=title;
		this.legend = legendList;
		this.category = categoryList;
		this.series = seriesList;
	}

}


public class Series {
	public String name;
	public String type;
	public List<Integer> data;
	public Series(String name,String type,List<Integer> data) {
		// TODO Auto-generated constructor stub
		this.name = name;
		this.type = type;
		this.data = data;
	}

}

由于主要是为了看echart的动态加载过程,所以没有从数据库中直接取数,而是直接赋值进行操作的,如果是在实际项目中,用一个java bean,一个mabitis的mapper和一个service就可以直接获取到相关的数据集,再利用for循环进行循环导入就可以了。

echartGetData()传出的json数据为:

{
    "title": "折线图和柱图",
    "category": ["一月份", "二月份", "三月份", "四月份"],
    "legend": ["销量", "售价"],
    "series": [{
        "name": "销量",
        "type": "line",
        "data": [11, 24, 31, 14]
    }, {
        "name": "售价",
        "type": "bar",
        "data": [31, 44, 51, 36]
    }]
}

实际加载数据后的echart图

由于折线图和柱图都比较简单,其data直接传一个数组就可以了,而在pie图以及一些其他复杂的一些echart图往往比较复杂,需要在数组中嵌套map,然后主要就是在哪里取数和处理,比如我这次pie图就是在后端直接处理好,然后传给前端一个封装好的Json,这样处理起来相对更加方便,话不多说,首先看pie图的前端处理

<script>
    var myChart = echarts.init(document.getElementById("pieChart"));
    option = {
        title: {
            text: '某站点用户访问来源',
            subtext: '纯属虚构',
            x: 'center'
        },
        tooltip: {
            trigger: 'item',
            formatter: "{a} <br/>{b} : {c} ({d}%)"
        },
        toolbox: {     //工具栏
            feature: {
                dataView: {readOnly: false},
                restore: {},
                saveAsImage: {}
            }
        },
        legend: {
            orient: 'vertical',
            left: 'left',
            data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎']
        },
        series: [
            {
                name: '访问来源',
                type: 'pie',
                // radius: ['55%','70%'],
                radius: '55%',
                center: ['50%', '60%'],
                data: [
                    {value: 335, name: '直接访问'},
                    {value: 310, name: '邮件营销'},
                    {value: 234, name: '联盟广告'},
                    {value: 135, name: '视频广告'},
                    {value: 1548, name: '搜索引擎'}
                ]
            }
        ]
    };
    myChart.setOption(option);
    myChart.hideLoading();
    getChartData();
    function getChartData() {
        var options = myChart.getOption();
        $.ajax({
            type: "post",
            sync: false,
            url: "urlurlrul/getPieChartData",
            data: {},
            dataType: "json",
            success: function (result) {
                options.title[0].text=result.title;
                options.legend[0].data = result.legend;
                options.series[0].name = result.series[0].name;
                options.series[0].data = result.series[0].data;
                myChart.hideLoading();
                myChart.setOption(options);
            }
        })
    }
</script>

可以看出我的data直接接收的就是可以直接使用的json,不需要再重新循环导入,当然如果在这里循环导入也是可以的,只是后端的处理方式不同而已。然后后端的处理是这样的

@PostMapping(value="/getPieChartData",produces="application/json;charset=utf-8")
	public EchartData getPieChartData() {
		String title = "Pie图和Doughnut图";
		List<String> category = new ArrayList<>();
		List<String> legend = new ArrayList<>();
		legend.add("Cola");
		legend.add("Wine");
		legend.add("Tea");
		legend.add("GreenTea");

		List<Object> dataList = new ArrayList<>();
		Map<String,Object> data0= new HashMap<>();
		data0.put("name","Cola");
		data0.put("value",32);
		dataList.add(JSONObject.fromObject(data0));
		data0.put("name","Wine");
		data0.put("value",23);
		dataList.add(JSONObject.fromObject(data0));
		data0.put("name","Tea");
		data0.put("value",12);
		dataList.add(JSONObject.fromObject(data0));
		data0.put("name","GreenTea");
		data0.put("value",35);
		dataList.add(JSONObject.fromObject(data0));
	
		
		List<Object> pieSeries = new ArrayList<>();
		pieSeries.add(new PieSeries("饮料分布",dataList));
		EchartData echartData = new EchartData(title,category,legend,pieSeries);
		return echartData;
	}


public class PieSeries {
	public String name;
//	public Map<String,Integer> data;
	public List<Object> data;
	public PieSeries(String name,List<Object> obj) {
		// TODO Auto-generated constructor stub
		this.name= name;
		this.data=obj;
	}

}

getPieChartData传出的json文件为:

{
    "title": "Pie图和Doughnut图",
    "legend": ["Cola", "Wine", "Tea", "GreenTea"],
    "series": [{
        "name": "饮料分布",
        "data": [{
            "name": "Cola",
            "value": 32
        }, {
            "name": "Wine",
            "value": 23
        }, {
            "name": "Tea",
            "value": 12
        }, {
            "name": "GreenTea",
            "value": 35
        }]
    }]
}

加载之后的echart图表为:

这种后端处理后,直接向前端传处理好的json数据,实际用起来是非常方便的。另外对于饼图而言,其数据结构与Douhnut图和南丁格尔玫瑰图其实格式都是非常类似的,基本上一种就可以覆盖好几种的写法。其实这里最主要的麻烦是数据的格式问题,后端怎么传以及前端怎么处理,如果没有很好的需求文档,配合起来是非常痛苦的,所以最好的办法就是自己都会,自己都干了就行了(~.~)
有了这么一个练习基础,可以进行一个比较复杂的练习,其实也不复杂,只是看起来内容更加丰富而已。就是下面这个图

这是echart社区的一个例子,把它应用在项目中试试。这个图有两个比较重要的点,其一就是map图的处理,这种格式一般都会在输入端有所限制,只允许导入省份之类的信息,而且由于矢量信息存在问题,目前已经不支持省级以下比如市级甚至更低的缩放。其二就是这个例子给了我们一个非常好的思路,就是他将两种数据展示图用于展示同一组数据,这个功能的实现关键就在于echart 每个功能块强大的功能,如果仔细观察,我们发现上面无论是Legend,series等功能块其实都是数组形式的,而我们对于series这部分就可以定义不同的数据展现形式,比如bar、pie之类的,剩余的就是一些美化工作了。

当然,如果对于map这个图进一步进行仔细研究就会发现,还有很多需要进行进一步优化的,比如颜色范围的调节如何与实际的输入契合,避免局部大数导致整体没有区分。然后还有就是数据的排序,我们可以看出右边本意是为了进行排序,将前十列出来,但是这个一般是在后端数据有意义的情况下才会进行的,所以后续的工作还有很多。

首先,先看一下其js代码

<!DICTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <title>ECharts图</title>
    <script src="js/echarts.min.js" type="text/javascript"></script>
    <script src="js/china.js" type="text/javascript"></script>
    <!--    <script src="http://echarts.baidu.com/build/dist/echarts.js">-->
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
</head>
<body>

<div id="MapChart" style="height: 800px;width:1000px;border: 6px solid #ccc9cb; padding: 100px;"></div>


<script>


    var myChart = echarts.init(document.getElementById('MapChart'));


    var data = [{
        name: '江苏省',
        value: 5.3
    },
        {
            name: '北京市',
            value: 3.8
        },
        {
            name: '上海',
            value: 4.6
        },
        {
            name: '重庆',
            value: 3.6
        },
        {
            name: '河北',
            value: 3.4
        },
        {
            name: '河南',
            value: 3.2
        },
        {
            name: '云南',
            value: 1.6
        },
        {
            name: '辽宁',
            value: 4.3
        },
        {
            name: '黑龙江',
            value: 4.1
        },
        {
            name: '湖南',
            value: 2.4
        },
        {
            name: '安徽',
            value: 3.3
        },
        {
            name: '山东',
            value: 3.0
        },
        {
            name: '新疆',
            value: 1
        },
        {
            name: '江苏',
            value: 3.9
        },
        {
            name: '浙江',
            value: 3.5
        },
        {
            name: '江西',
            value: 2.0
        },
        {
            name: '湖北',
            value: 2.1
        },
        {
            name: '广西',
            value: 3.0
        },
        {
            name: '甘肃',
            value: 1.2
        },
        {
            name: '山西',
            value: 3.2
        },
        {
            name: '内蒙古',
            value: 3.5
        },
        {
            name: '陕西',
            value: 2.5
        },
        {
            name: '吉林',
            value: 4.5
        },
        {
            name: '福建',
            value: 2.8
        },
        {
            name: '贵州',
            value: 1.8
        },
        {
            name: '广东',
            value: 3.7
        },
        {
            name: '青海',
            value: 0.6
        },
        {
            name: '西藏',
            value: 0.4
        },
        {
            name: '四川',
            value: 3.3
        },
        {
            name: '宁夏',
            value: 0.8
        },
        {
            name: '海南',
            value: 1.9
        },
        {
            name: '台湾',
            value: 0.1
        },
        {
            name: '香港',
            value: 0.1
        },
        {
            name: '澳门',
            value: 0.1
        }
    ];

    var yData = [];
    var barData = [];

    for (var i = 0; i < 5; i++) {
        barData.push(data[i]);
        yData.push(i + data[i].name);
    }
    var option = {
        title: [{
            show: true,
            text: '排名情况',
            textStyle: {
                color: '#2D3E53',
                fontSize: 18
            },
            right: 180,
            top: 100
        }],
        tooltip: {
            show: true,
            formatter: function(params) {
                return params.name + ':' + params.data['value'] + '%'
            },
        },
        visualMap: {
            type: 'continuous',
            orient: 'horizontal',
            itemWidth: 10,
            itemHeight: 80,
            text: ['高', '低'],
            showLabel: true,
            seriesIndex: [0],
            min: 0,
            max: 2,
            inRange: {
                color: ['#6FCF6A', '#FFFD64', '#FF5000']
            },
            textStyle: {
                color: '#7B93A7'
            },
            bottom: 30,
            left: 'left',
        },
        grid: {
            right: 10,
            top: 135,
            bottom: 100,
            width: '20%'
        },
        xAxis: {
            show: false
        },
        yAxis: {
            type: 'category',
            inverse: true,
            nameGap: 16,
            axisLine: {
                show: false,
                lineStyle: {
                    color: '#ddd'
                }
            },
            axisTick: {
                show: false,
                lineStyle: {
                    color: '#ddd'
                }
            },
            axisLabel: {
                interval: 0,
                margin: 85,
                textStyle: {
                    color: '#455A74',
                    align: 'left',
                    fontSize: 14
                },
                rich: {
                    a: {
                        color: '#fff',
                        backgroundColor: '#FAAA39',
                        width: 20,
                        height: 20,
                        align: 'center',
                        borderRadius: 2
                    },
                    b: {
                        color: '#fff',
                        backgroundColor: '#4197FD',
                        width: 20,
                        height: 20,
                        align: 'center',
                        borderRadius: 2
                    }
                },
                formatter: function(params) {
                    if (parseInt(params.slice(0, 1)) < 3) {
                        return [
                            '{a|' + (parseInt(params.slice(0, 1)) + 1) + '}' + '  ' + params.slice(1)
                        ].join('\n')
                    } else {
                        return [
                            '{b|' + (parseInt(params.slice(0, 1)) + 1) + '}' + '  ' + params.slice(1)
                        ].join('\n')
                    }
                }
            },
            data: yData
        },
        geo: {
            // roam: true,
            map: 'china',
            left: 'left',
            right: '300',
            // layoutSize: '80%',
            label: {
                emphasis: {
                    show: false
                }
            },
            itemStyle: {
                emphasis: {
                    areaColor: '#fff464'
                }
            }
        },
        series: [{
            name: 'mapSer',
            type: 'map',
            roam: false,
            geoIndex: 0,
            label: {
                show: false,
            },
            data: data
        }, {
            name: 'barSer',
            type: 'bar',
            roam: false,
            visualMap: false,
            zlevel: 2,
            barMaxWidth: 8,
            barGap: 0,
            itemStyle: {
                normal: {
                    color: function(params) {
                        // build a color map as your need.
                        var colorList = [{
                            colorStops: [{
                                offset: 0,
                                color: '#FFD119' // 0% 处的颜色
                            }, {
                                offset: 1,
                                color: '#FFAC4C' // 100% 处的颜色
                            }]
                        },
                            {
                                colorStops: [{
                                    offset: 0,
                                    color: '#00C0FA' // 0% 处的颜色
                                }, {
                                    offset: 1,
                                    color: '#2F95FA' // 100% 处的颜色
                                }]
                            }
                        ];
                        if (params.dataIndex < 3) {
                            return colorList[0]
                        } else {
                            return colorList[1]
                        }
                    },
                    barBorderRadius: 15
                }
            },
            data: barData
        }]
    };

    myChart.setOption(option)

    myChart.hideLoading();
    getChartData();

    function getChartData(){
        var options = myChart.getOption();
        $.ajax({
            type: "post",
            async: false, //同步执行
            url: "urlurlrul/getMapChartData",
            data: {},
            dataType: "json", //返回数据形式为json
            success: function (result) {
                if (result) {

                    options.series[0].data = result.series;
                    var tempData = result.series;
                    var yData = [];
                    var barData = [];

                    for (var i = 0; i < result.series.length; i++) {
                        barData.push(result.series[i]);
                        yData.push(i + result.series[i].name);
                    }
                    options.yAxis[0].data=yData;
                    options.series[1].data = barData;
                    myChart.hideLoading();
                    myChart.setOption(options);
                }
            },
            error: function (errorMsg) {
                alert("图表请求数据失败!");
                myChart.hideLoading();
            }
        });
    }


</script>



<script type="text/javascript">

</script>
</body>
</html>

其实这两部分的数据来源都是相同的,所以可以使用同一个数据元,然后进行进一步的处理就可以了。

后端的controller 为:

@PostMapping(value="/getMapChartData",produces="application/json;charset=utf-8")
	public EchartData getMapChartData() {
		String title ="MapChartData";
		List<String> category = new ArrayList<>();
		List<String> Legend = new ArrayList<>();
		List<Object> mapDataList = new ArrayList();
		Map<String,Object> map = new HashMap<>();
		map.put("name", "山西");
		map.put("value", 1.3);
		mapDataList.add(JSONObject.fromObject(map));
		map.put("name","北京");
		map.put("value",7.8);
		mapDataList.add(JSONObject.fromObject(map));
		map.put("name","浙江");
		map.put("value",3.2);
		mapDataList.add(JSONObject.fromObject(map));
		map.put("name","上海");
		map.put("value",6.3);
		mapDataList.add(JSONObject.fromObject(map));
		map.put("name","西藏");
		map.put("value",0.6);		
		mapDataList.add(JSONObject.fromObject(map));
		map.put("name","天津");
		map.put("value",0.1);
		mapDataList.add(JSONObject.fromObject(map));		
		EchartData echartData = new EchartData(title,category,Legend,mapDataList);
		return echartData;
	}

这个处理是非常简单的,当然也很粗糙,需要进一步的优化,我们先看一下传出的json:

{
    "title": "MapChartData",
    "series": [{
        "name": "山西",
        "value": 1.3
    }, {
        "name": "北京",
        "value": 7.8
    }, {
        "name": "浙江",
        "value": 3.2
    }, {
        "name": "上海",
        "value": 6.3
    }, {
        "name": "西藏",
        "value": 0.6
    }, {
        "name": "天津",
        "value": 0.1
    }]
}

其最终显示效果:

基本功能算是实现了,后续的工作就是在实际的项目中实际的优化了。

 

这三个图例加上最后的整理总结,大概花了一天的时间,其实最主要的问题还是数据格式的问题,但是多看一些例子之后,自己也慢慢学会了到底该如何处理类似的数据。除了对echart图有了一丝的了解,也对前后端的交互有了更深层次的理解,同时,我觉得自己收获最大的应该是似乎在与echart的开发者对话,理解了某些巧妙的设计,的确佩服,自己不是做前端的,写js对于我来说算是一个挑战,虽然简单的可以读懂,写起来还是没那么容易。如果有想进一步交流的,欢迎评论(~.~)


版权声明:本文为HepBen原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。