本例中,我们要做的是,
在原有echart表格的基础上,添加新的数据显示。
【大致思想】
分析原有表格中某项数据的显示原理,根据此原理,将我们所需要的数据展现出来。
即,最关键的就是分析数据库的数据,是通过一个怎样的过程,以图表的形式展现的。
第一步、分析
这一步,我们直到最后做出来,才刚刚搞清楚我们需要的原理。也即是说,这一步其实是最重要的。
接下来我们首先叙述下分析原理过程中的问题,然后再解释原理。
【问题】
我们依样画葫芦试着做的时候,遇到一个比较大的问题是,页面不停的刷新,刷新了一下午也刷不出最后的结果。
这里的原因可能有两点,
一者,
js错误,即,JSP页面不显示的原因,是JSP页面的代码出错了。
显而易见,显示的部分应当是不会出错的。因为我们根本就不需要动这部分。
JSP代码中的另一部分,js是我们改过的,所以,分析可能是这里出错。
二者,
后台出错,即,Controller中的某部分代码出错了。因为我们改了这部分里的一些代码。
【分析问题】
以上两个可能的问题,我们需要确定是哪一个。
我们首先需要知道,
前者js错误需要在F12控制台选项的js菜单中观察,我们怀疑js出错的时候,往往要到这里看有没有报错。
后者Controller代码报错,是在Eclipse的控制台中报错的。
最后我们分析得知,是js报错,但根本原因还是Controller中的代码错了。所以是二者都有错!
但js的报错信息,告诉我们哪里错了,我们从报错的地方追溯回去,才发现了问题所在。
【原理】
因为我们做的这个例子,是要在图表中显示新加的数据。新加的,我们要做的事情有人已经做过了。
我们要做的,要分析已经实现的部分的原理。
【数据流】
这里提出一个私人定义的概念,数据流。
即分析,我们看到的东西是从哪里来的。将这条路线画出来。正向推,反向推都是可以的。
接下来我们采用反向推!
以最接近我们要求的注册量为例:
{ /* 注册量 */ name : chart_line_names[i][0], type : 'line', data : data.list[i].data[0], markPoint : { data : [ { type : 'max', name : '最大值' }, { type : 'min', name : '最小值' } ] }, markLine : { data : [ { type : 'average', name : '平均值' } ] } },
这里涉及到ECharts的知识。我们发现,图中的这条数据线的显示,所需的数据来自data。
即
data : data.list[i].data[0]再分析,data中的data是哪里来的
function base_member_regist_cnt_doChart(data){来自于这里,画图的js函数doChart所需的数据都来自于这个方法传进来的参数data
再分析,这个data是哪里来的
var jsonObj= data.chartDatas; base_member_regist_cnt_doChart(jsonObj);来自于jsonObj,jsonObj又来自于data.chartDatas
分析这里的data是哪儿的。【注意:我们分析数据来源时,只分析源,不需要管data.balabala里的balabala这些东西】
$.getJSON(base_member_regist_cnt_url, paramdata, function (data) {我们发现data是通过访问链接得到的JSON数据。
分析下paramdata是哪里来的。
function base_member_regist_cnt_genJson(paramdata){
var $form = $(form); var paramdata = $form.serializeArray(); base_member_regist_cnt_genJson(paramdata);我们发现paramdata来自于form。
<form id="form1" action="chart/base_member_regist_cnt.html" method="get" onsubmit="return base_member_regist_cnt_submit(this);"> <div class='ichartjs_details' style="margin-left:200px;margin-bottom:10px"> 日期:<input type="text" id="base_member_regist_cnt_startDate" name="startDate" class="date" minDate="2010-07-15" value="" maxDate="{%y}-%M-{%d}" /> 与 <input type="text" id="base_member_regist_cnt_compareDate" name="compareDate" class="date" minDate="2010-07-15" value="" maxDate="{%y}-%M-{%d}" /> 与 前<select name="intervalDay" id="base_member_regist_cnt_intervalDay"> <option value="3" >3日</option> <option value="7" >7日</option> <option value="30">1个月</option> </select>平均值 <button type="submit">比较</button><br /> </div> </form>也即是,来自于表单的提交。
我们如果想得到这个JSON数据,就需要传递paramdata,才能够得到。
@RequestMapping("/base_member_regist_cnt.json") public String base_member_regist_cnt(String startDate,String compareDate, String intervalDay, ModelMap modelMap) throws MyException, ParseException {我们找到了要访问的链接。而且这个链接的确是返回了一个jsonView。
也就是说,这个名为base_member_regist_cnt的方法中,在jsonView中装入了数据。
我们接下来就是要分析,它是怎么在这个方法里取得数据并把它装入jsonView的。
该方法的精简版方法体如下
if(startDate == null || startDate.trim().length() == 0) { startDate=dateUtil.getString(new Date(), "yyyy-MM-dd"); } if(intervalDay == null || intervalDay.trim().length() == 0) { intervalDay="7"; } if(compareDate == null || compareDate.trim().length() == 0) { Date date=dateUtil.addDay(new Date(), -Integer.valueOf(intervalDay)); compareDate=dateUtil.getString(date, "yyyy-MM-dd"); } ParamMap paramMap=new ParamMap(); paramMap.put("startDate", startDate); paramMap.put("compareDate", compareDate); paramMap.put("intervalDay", intervalDay); EChartsData eChartsData=new EChartsData(paramMap); String sqlStr; String[] hoursLabels = LabelUtil.getLabelByIntBegin_00(24); ECharts eCharts2 = new ECharts(hoursLabels); //当日注册数 List<Map<String,Object>> list2; sqlStr = stringUtil.formatMsg( "select DATE_FORMAT(b.CREATED_DATE,'%H') as label,count(MEMBER_ID) as STARTDATE_COUNT from moyoyo_member.BASE_INFO b where DATE_FORMAT(b.CREATED_DATE,'%Y-%m-%d') = '{0}' group by DATE_FORMAT(b.CREATED_DATE,'%H')", new Object[]{startDate}); list2 = chartDataService.getChartList(sqlStr); eCharts2.add(LabelUtil.getValue(list2, hoursLabels,"label","STARTDATE_COUNT")); List<Map<String,Object>> list21; sqlStr = stringUtil.formatMsg( "select DATE_FORMAT(b.CREATED_DATE,'%H') as label,count(MEMBER_ID) as COMPAREDATE_COUNT from moyoyo_member.BASE_INFO b where DATE_FORMAT(b.CREATED_DATE,'%Y-%m-%d') = '{0}' group by DATE_FORMAT(b.CREATED_DATE,'%H')", new Object[]{compareDate}); list21 = chartDataService.getChartList(sqlStr); eCharts2.add(LabelUtil.getValue(list21, hoursLabels,"label","COMPAREDATE_COUNT")); Date date=dateUtil.getDateObj(startDate); date=dateUtil.addDay(date, -Integer.valueOf(intervalDay)); String compareStartDate=dateUtil.getString(date, "yyyy-MM-dd"); List<Map<String,Object>> list22; sqlStr = stringUtil.formatMsg("select DATE_FORMAT(b.CREATED_DATE,'%H') as label,round(count(MEMBER_ID)/{0},0) as AVG_COUNT from moyoyo_member.BASE_INFO b where b.CREATED_DATE >= '{1}' and b.CREATED_DATE<'{2}' group by DATE_FORMAT(b.CREATED_DATE,'%H')" , new Object[]{intervalDay,compareStartDate,startDate}); list22 = chartDataService.getChartList(sqlStr); eCharts2.add(LabelUtil.getValue(list22, hoursLabels,"label","AVG_COUNT")); eChartsData.add(eCharts2); modelMap.addAttribute("chartDatas", eChartsData); return "jsonView"; }根据以上方法体的代码, 我们依然反向来推!
jsonView 的数据肯定是来自于modelMap
modelMap.addAttribute("chartDatas", eChartsData);
modelMap中的数据来自于eChartsData。
eChartsData中的又装入了eCharts2。
eChartsData.add(eCharts2);而eCharts2中的数据又是eCharts2 add了三次得到的。
eCharts2.add(LabelUtil.getValue(list2, hoursLabels,"label","STARTDATE_COUNT"));
eCharts2.add(LabelUtil.getValue(list21, hoursLabels,"label","COMPAREDATE_COUNT"));
eCharts2.add(LabelUtil.getValue(list22, hoursLabels,"label","AVG_COUNT"));而add的内容中又包含了list2,list21,list22三个list。
list2 = chartDataService.getChartList(sqlStr);
list21 = chartDataService.getChartList(sqlStr);
list22 = chartDataService.getChartList(sqlStr);而每个list又是通过sqlStr取得的数据。
这样看来,
方法这种不变的东西,如果重复了很多遍,我们只需要分析一次就可以,我们需要找的,是这一个方法追溯到最后,
不一样的那个参数,而这个参数绝对是很明显的那种。
sqlStr = stringUtil.formatMsg( "select DATE_FORMAT(b.CREATED_DATE,'%H') as label,count(MEMBER_ID) as STARTDATE_COUNT from moyoyo_member.BASE_INFO b where DATE_FORMAT(b.CREATED_DATE,'%Y-%m-%d') = '{0}' group by DATE_FORMAT(b.CREATED_DATE,'%H')", new Object[]{startDate});就像这样!而这里就是我们得到数据的地方。SQL从数据库中取得数据。
至此,我们要分析的原理,其实就是数据流已经分析完成了。
我们看到了数据从哪里来,到哪里去。
我们要注意的是,没有分析到最明显的参数那一步时,就不算分析完,因为还没有找到数据的源头。
【知识】
JSON格式的数据。什么是JSON数据?
我们在分析代码的时候,怀疑过为什么直接return一个jsonView就可以返回数据呢?
这个JSONVIEW到底是什么东西。经过研究,我们可以知道:
json是一种轻量级的数据交换格式。可以是一堆键值对,也可以是一堆数组。这里我们没有分析出它是什么?
但是没有关系,只需要知道json数据就是可以封装一大堆数据的数据对象。
否则,我们在JSP中画图的时候为什么总是传递JSON对象呢?23333333333
第二步、搭建框架
我们从需求就可以知道,我们要改动的关键地方就是SQL。
我们需要增加多少个数据,就按原理分析的来,增加多少个一样的部分。
这些一样的部分!是体力活!最后我们才专注于改动SQL。
第三步、改动SQL
这里的SQL又涉及到了联表查询。又是我们比较头疼的这种联表查询。
本例中,其实我们实际做的时候是很快就把SQL写好了,只是一直不太确定是不是正确的。
这里如何判断是否SQL正确呢?
一个最好用且最常用的办法,【我们也是今天才明确这个东西的作用】
使用Navicat的查询功能。
我们看下Navicat的界面,发现查询选项是在某个数据库的菜单下的,这个数据库中有很多的表。
我们要做的联表查询,肯定是在一个数据库中做的。
所以我们活用这个查询功能。
这里再提出一个私人的定义。
【SQL调试器】
我们可以把Navicat的查询功能当做一个SQL的调试器,我们不确定写出来的SQL是不是对的,
换句话说,就是它能不能够取到数据,取到的数据是否正确,这些东西,
查询之后,是可以直接在界面中看到的。如果有错,选择不到我们需要的数据,随时修改就可以。
而且这样做还有一个好处,Navicat中有美化SQL格式的功能。
我们可以更加清晰的看到SQL的语法,如果哪里需要修改,也很清晰明了。
最终我们用来联表查询的SQL例子如下:
SELECT DATE_FORMAT(b.CREATED_DATE, '%H') AS label, count(d.MEMBER_ID) AS STARTDATE_COUNT FROM moyoyo_member.BASE_INFO b LEFT JOIN moyoyo_member.DETAIL_INFO d ON b.MEMBER_ID = d.MEMBER_ID WHERE DATE_FORMAT(b.CREATED_DATE, '%Y-%m-%d') = '{0}' AND d.CHANNEL_TYPE = 1 GROUP BY DATE_FORMAT(b.CREATED_DATE, '%H')发现其实意外的简单。其它几个SQL,只要照猫画虎,按照原来的SQL来写就可以啦。
第四步、画图
接下来,到了最为关键的一步。画图!
我们拿到了数据,并且传回来了。怎么来画这个图呢?
还是分析原来已经画好的图的原理。
function base_member_regist_cnt_doChart(data){ $("input#base_member_regist_cnt_startDate").val(data.paramMap['startDate']); $("input#base_member_regist_cnt_compareDate").val(data.paramMap['compareDate']); $("#base_member_regist_cnt_intervalDay").val(data.paramMap['intervalDay']); chart_titles = ['用户注册情况','当日注册数量比较']; chart_line_names = [ ['总数'], [data.paramMap['startDate']+' 注册量', data.paramMap['startDate']+' WAP 注册量', data.paramMap['startDate']+' WEB 注册量', data.paramMap['startDate']+' APP 注册量', data.paramMap['startDate']+' SDK 注册量', data.paramMap['compareDate']+' 注册量', data.paramMap['compareDate']+' WAP 注册量', data.paramMap['compareDate']+' WEB 注册量', data.paramMap['compareDate']+' APP 注册量', data.paramMap['compareDate']+' SDK 注册量', '前'+data.paramMap['intervalDay']+'天平均注册量', '前'+data.paramMap['intervalDay']+'天 WAP 平均注册量', '前'+data.paramMap['intervalDay']+'天 WEB 平均注册量', '前'+data.paramMap['intervalDay']+'天 APP 平均注册量', '前'+data.paramMap['intervalDay']+'天 SDK 平均注册量', ] ];画图主要靠这个js函数,doChart来完成。
先定义了一堆title和图例的名字。。。。
接下来,要画图的data传递进来了,这个函数是如何处理这个data的呢?
i = 1 if(data.list[i].data[0].length==0){ chart_div = document.getElementById('base_member_regist_cnt_canvasDiv' + i) html = ''; html+='<div style="font-size: 14px;text-align: center; width: 100%;height:100%;color:red">' html+= data.paramMap["startDate"]+ chart_titles[i] + ' 暂时没有数据' html+= '</div>' chart_div.innerHTML=html; } if(data.list[i].data[0].length>0){ echarts.init(document.getElementById('base_member_regist_cnt_canvasDiv' + i)).setOption({ title : { text : data.paramMap["startDate"]+ chart_titles[i], x : "center" }, tooltip : { trigger : 'axis' }, legend : { data : chart_line_names[i], y : 'bottom' }, toolbox : { show : true, feature : { dataView : { show : true, readOnly : false }, magicType : { show : true, type : [ 'line', 'bar' ] }, restore : { show : true }, saveAsImage : { show : true } } }, calculable : true, xAxis : [ { type : 'category', data : data.list[i].labels } ], yAxis : [ { type : 'value', scale : true, name : '注册数量', axisLabel : { formatter : '{value} 个' } } ], series : [ { /* 注册量 */ name : chart_line_names[i][0], type : 'line', data : data.list[i].data[0], markPoint : { data : [ { type : 'max', name : '最大值' }, { type : 'min', name : '最小值' } ] }, markLine : { data : [ { type : 'average', name : '平均值' } ] } },到这一步,完全就是ECharts的知识了。
如何定义坐标轴,如何定义图例,如何定义标题等等等等。
这些东西都已经写好了,我们只需要添加新的数据进去就可以了。
经过分析,我们可以知道,series里面是一条一条的数据。我们新添加一项数据,就是在这里添加。
根据特定的格式添加就去就好了,很简单!
【图例问题】
图画出来以后,我们发现有个问题,图例过于混乱了,
如何整理这个图例呢?
【图例相关的修改】
要整理图例,要修改的是-------图例相关代码!
legend : { data : chart_line_names[i], textStyle:{fontSize:12}, //x:'left', //y:'50px', //orient:'vertical', y : '365px', },在整理这个图例的过程中,我们主要修改了legend(英文意思就是传奇、图例)
中的x、y,即图例的位置。因为图例最开始与图表重合了,我们将图例向下移动,即调整y的坐标,最终发现y的坐标为365px比较合适。
这个365px是图例与图表顶端的距离。
后来发现怎么修改都不合适,只能调整图例字体的大小,
textStyle:{fontSize:12},才调整到了一个比价合适的状态,如下!
另一种方法,其实可以不用修改字体的大小,转而修改字的多少,把图例简化,如:
2015-11-23注册量改为11-23,就可以节省不少空间。该例中,图例不合适的原因就是太多,占的空间也太多了!
修改的过程中,我们用到了js取子字符串的知识。
【js取子字符串】
data.paramMap['startDate'].substr(5,5),即substr函数,表示从第几位开始,取几位数!
【js检测数据的传送】
这个问题我们之前就说过,这里又用到了,但是没有想起来用,所以再重申一遍。
alert()的用法。相当于Java代码中的System.out.println,相当于Python代码中的print。
加了alert(),我们刷新页面的时候,就会自动跳出包含了数据信息的提示框。
【ECharts表格的默认选中或默认不选中】
接下来我们要实现另一个效果,即,刚刷出页面的时候,需要只显示前五条记录。其它的数据默认不显示。
我们从ECharts的官方网站例子中了解到应当在legend的selected中进行设置。
option = { legend: { orient: 'horizontal', // 'vertical' x: 'right', // 'center' | 'left' | {number}, y: 'top', // 'center' | 'bottom' | {number} backgroundColor: '#eee', borderColor: 'rgba(178,34,34,0.8)', borderWidth: 4, padding: 10, // [5, 10, 15, 20] itemGap: 20, textStyle: {color: 'red'}, selected: { '降水量' : false }, data: [ { name:'蒸发量', icon : 'image://../asset/ico/favicon.png', textStyle:{fontWeight:'bold', color:'green'} }, '降水量','最高气温', '最低气温' ] },其中,将'降水量'这一项设置为false,刷新页面的时候降水量这一项就会默认不选中。
本例中,我们的图例很多,而且图例的名字不是死的,而是由每天的日期与其它字符串组成的。
chart_line_names = [ ['总数'], [data.paramMap['startDate'].substr(5,5), data.paramMap['startDate'].substr(5,5)+' WAP', data.paramMap['startDate'].substr(5,5)+' WEB', data.paramMap['startDate'].substr(5,5)+' APP', data.paramMap['startDate'].substr(5,5)+' SDK', <pre name="code" class="javascript"> data.paramMap['compareDate'].substr(5,5), <pre name="code" class="javascript"> data.paramMap['compareDate'].substr(5,5)+' WAP', data.paramMap['compareDate'].substr(5,5)+' WEB', data.paramMap['compareDate'].substr(5,5)+' APP', data.paramMap['compareDate'].substr(5,5)+' SDK', '前'+data.paramMap['intervalDay']+'天平均', '前'+data.paramMap['intervalDay']+'天 WAP 平均', '前'+data.paramMap['intervalDay']+'天 WEB 平均', '前'+data.paramMap['intervalDay']+'天 APP 平均', '前'+data.paramMap['intervalDay']+'天 SDK 平均', ] ];官方的例子中,这种效果实现时,图例名字是死的,直接写出来。
这里如果我们直接传参数进去,
if(data.list[i].data[0].length>0){<pre name="code" class="javascript">echarts.init(document.getElementById('base_member_regist_cnt_canvasDiv' + i)).setOption({ title : { text : data.paramMap["startDate"]+ chart_titles[i], x : "center" }, tooltip : { trigger : 'axis' }, legend : { data : chart_line_names[i], textStyle:{fontSize:12}, //x:'left', //y:'50px', //orient:'vertical', y : '365px', selected:{ chart_line_names[1][0]:false } }, });这种写法,没有达到我们想要的效果。
即,
官方的写法只适用于图例名字死的时候,
如果图例名字为变量,这样写是不行的,因为这个变量的值,传不到legend中的selected中去。
最终,我们只能改变代码的结构来达到目的。如下:
先定义一个option
option= { title : { text : data.paramMap["startDate"]+ chart_titles[i], x : "center" }, tooltip : { trigger : 'axis' }, legend : { data : chart_line_names[i], textStyle:{fontSize:12}, //x:'left', //y:'50px', //orient:'vertical', y : '365px', selected:{} },
option.legend.selected[chart_line_names[1][5]] = false; option.legend.selected[chart_line_names[1][6]] = false; option.legend.selected[chart_line_names[1][7]] = false; option.legend.selected[chart_line_names[1][8]] = false; option.legend.selected[chart_line_names[1][9]] = false; option.legend.selected[chart_line_names[1][10]] = false; option.legend.selected[chart_line_names[1][11]] = false; option.legend.selected[chart_line_names[1][12]] = false; option.legend.selected[chart_line_names[1][13]] = false; option.legend.selected[chart_line_names[1][14]] = false; echarts.init(document.getElementById('base_member_regist_cnt_canvasDiv' + i)).setOption(option); }即先定义好option,然后再定义option中的legend中的selected属性。
这样最终可以达到我们想要的效果。
【注意】
这样的修改,代码结构发生了变化。还有一个地方需要注意,
我们要在option定义完后再修改legend中的selected属性值,需要现在option中声明一下有selected这么个属性。
legend : { data : chart_line_names[i], textStyle:{fontSize:12}, //x:'left', //y:'50px', //orient:'vertical', y : '365px', selected:{} },否则会js报错,option.legend.selected没有被定义。
修改过这个图例以后的效果如下:
接下来我们要做的另外一件事情,是自定义tooltip,我们现在的代码中,
tooltip是默认样式的,
tooltip : { trigger : 'axis' },即跟着图例走。
我们想定义我们想要的悬浮框,就需要自己来写!
【自定义ECharts悬浮框样式】
实现这个自定义的悬浮框,我们参考的是ECharts官方的例子代码,最终成功实现!
官方的代码如下:
option = { tooltip : { // Option config. Can be overwrited by series or data trigger: 'axis', //show: true, //default true showDelay: 0, hideDelay: 50, transitionDuration:0, backgroundColor : 'rgba(255,0,255,0.7)', borderColor : '#f50', borderRadius : 8, borderWidth: 2, padding: 10, // [5, 10, 15, 20] position : function(p) { // 位置回调 // console.log && console.log(p); return [p[0] + 10, p[1] - 10]; }, textStyle : { color: 'yellow', decoration: 'none', fontFamily: 'Verdana, sans-serif', fontSize: 15, fontStyle: 'italic', fontWeight: 'bold' }, formatter: function (params,ticket,callback) { console.log(params) var res = 'Function formatter : <br/>' + params[0].name; for (var i = 0, l = params.length; i < l; i++) { res += '<br/>' + params[i].seriesName + ' : ' + params[i].value; } setTimeout(function (){ // 仅为了模拟异步回调 callback(ticket, res); }, 1000) return 'loading'; } //formatter: "Template formatter: <br/>{b}<br/>{a}:{c}<br/>{a1}:{c1}" }, toolbox: { show : true, feature : { mark : {show: true}, dataView : {show: true, readOnly: false}, magicType : {show: true, type: ['line', 'bar', 'stack', 'tiled']}, restore : {show: true}, saveAsImage : {show: true} } }, calculable : true, xAxis : { data : ['周一','周二','周三','周四','周五','周六','周日'] }, yAxis : { type : 'value' }, series : [ { name:'坐标轴触发1', type:'bar', data:[ {value:320, extra:'Hello~'}, 332, 301, 334, 390, 330, 320 ] }, { name:'坐标轴触发2', type:'bar', data:[862, 1018, 964, 1026, 1679, 1600, 157] }, { name:'数据项触发1', type:'bar', tooltip : { // Series config. trigger: 'item', backgroundColor: 'black', position : [0, 0], formatter: "Series formatter: <br/>{a}<br/>{b}:{c}" }, stack: '数据项', data:[ 120, 132, { value: 301, itemStyle: {normal: {color: 'red'}}, tooltip : { // Data config. backgroundColor: 'blue', formatter: "Data formatter: <br/>{a}<br/>{b}:{c}" } }, 134, 90, { value: 230, tooltip: {show: false} }, 210 ] }, { name:'数据项触发2', type:'bar', tooltip : { show : false, trigger: 'item' }, stack: '数据项', data:[150, 232, 201, 154, 190, 330, 410] } ] };这个代码例子乍看很复杂,我们这里再提出一个私人的定义。
【代码简化法则】
有一个需求,我们需要编程来实现功能会有以下几种情况:
一者,从头开始写,这个时候我们需要寻找例子来参考,官方的或是私人的,有可能是比较复杂的例子;
二者,一部分有人已经做过了,本例就是这样。
这个时候我们需要做的就是理清楚逻辑。
看清楚代码的逻辑,我们要做的就是把代码简化到只剩逻辑!这就是简化!
比如以上这个自定义悬浮框的例子代码,我们可以去掉很多东西。
比如悬浮框的样式定制,字体定制,模拟异步回调等等。这些都是无关紧要的东西,我们可以去掉。
知道最后我们可以清楚的看到自定义悬浮框的核心代码,tooltip部分。
tooltip : { trigger : 'axis', formatter: function (s) { var res = 'Formatter:<br/>'+s[0].name; for (var i = 0, l = s.length; i < l; i++) { res += '<br/>' + s[i].seriesName + ' 注册量 : ' + s[i].value; } return res; } },这部分代码的意思是:
当鼠标移到光标时,会有数据传回来,装入参数s中,
然后这个function会执行下面的代码,实现对tooltip的定制。
这里的这个参数s,可以随意起名字,比如params等等。
这个参数之所以可以这样使用,我们可以类比AJAX的数据回调,function(data)这个data就可以随意起名字,
参数名字只不过是传回来的数据的一个容器,AJAX中不过是习惯性的起名为data而已。
最终我们的效果图如下!
截图快捷键:ctrl+alt+a