十八、从 jqPlot 到 Highcharts

Abstract

跟随 jqPlot 框架的脚步,一个新的 JavaScript 库正在迎头赶上,它叫做 Highcharts。这是一个商业产品,由挪威公司 Highsoft Solutions AS 于 2009 年底完成。在撰写本书时,这个新库的版本是 3.0.1,并且越来越多地作为图表专业表示的新解决方案在市场上提供。

跟随 jqPlot 框架的脚步,一个新的 JavaScript 库正在迎头赶上,它叫做 Highcharts。这是一个商业产品,由挪威公司 Highsoft Solutions AS 于 2009 年底完成。在撰写本书时,这个新库的版本是 3.0.1,并且越来越多地作为图表专业表示的新解决方案在市场上提供。

因为我们已经在很多方面提到了 jqPlot 的语法和结构,所以在这本书的第一部分末尾添加 Highcharts 库,专门用一章来介绍它,似乎是合适的。这个框架继承了 jqPlot 的所有优点:只需添加几行代码就可以在网页上实现一个完整的图表。Highcharts 还能够与许多其他 JavaScript 框架接口,包括 jQuery。就像 jqPlot 一样,它的语法简单、重要且直观,但不排除进一步扩展和定制的可能性。这还不是全部。

Highcharts 是一个专业产品,所以它远远超出了 jqPlot 库所能提供的范围。在这一章中,您将看到如何从您已经在 jqPlot 中看到的案例开始,向您的图表添加更多的特性。事实上,在第一个示例中,您将看到如何扩展已经很好的图形功能,这要归功于 jqPlot 库。您将通过进一步增强图表元素来做到这一点,比如工具提示、标记点和网格。此外,您将发现这个库如何包含许多主题,并了解如何将它们应用到您的图表中,以使它看起来总是与众不同。另一个例子说明了 Highcharts 库如何直接从文件中读取数据。最后,在本章的最后一部分,您将进一步创建三种非常特殊的图表,如下所示:

  • 主从图表——当您需要可视化大量数据,但希望一次只分析一个细节时,这是一个非常有用的解决方案。
  • 甘特图——一种特别适合于安排流程和项目的图表。
  • 组合图表—一个非常丰富的可视化工具,用于组合不同类型的图表。

多亏了 Highcharts 库,这三种图表类型才成为可能。因此,通过放置和逐步插入这些新特性,准备好在您离开 jqPlot 的地方熟悉这个漂亮的库。

高图表分布

关于 Highcharts 的使用,本产品有商业和免费的非商业许可。显然,Highcharts 库的免费使用仅限于个人和非营利目的。

在下载了构成发行版的文件包之后,您可以看到它包含了一大组文件。(更多信息见附录 A。)。)

主文件是highcharts.js,它是这个框架的真正核心,如果你想用 Highcharts 来绘制你的图表,在你的网页中包含这个文件是必不可少的。包含它的方式总是相同的,您可以通过两种方式完成:通过本地方法或内容交付网络(CDN)服务。

对于本地方法,请使用以下内容:

<script type="text/javascript" src="../src/js/highcharts.js"></script>

你有两个 CDN 服务选项。如果您想要最新的稳定版本,请写:

<script src="http://code.highcharts.com/highcharts.jsT2】

否则,如果您想要包含特定版本,请使用以下命令:

<script src="http://code.highcharts.com/3.0.5/highcharts.jsT2】

此外,除了highcharts.js之外,您还需要包含 jQuery,因为与 jqPlot 一样,它是基于那个库构建的,因此需要您访问它。即使在这种情况下,您也可以选择本地解决方案:

<script type="text/javascript" src="../src/js/jquery-1.9.1.js"></script>

或者 CDN 解决方案:

<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.jsT2】

</script>

然而,通过使用发行版中包含的一些适配器文件,也可以使用其他库作为基础而不是 jQuery。因此,您可以用 MooTools 或 Prototype 等库来替换 jQuery。

当您深入研究发行版时,您会看到另一个名为highcharts-more.js的文件。该文件包含与库的某些扩展和特殊情况相关的所有功能,例如,开发仪表、范围和极坐标图所需的功能。此外,有一组文件扮演着与 jqPlots 中的插件类似的角色,因为这些文件中的每一个都为您的图表添加了特定的功能。你可以在目录/js/modules中找到它们。表 18-1 描述了模块文件。

表 18-1。

The modules in the Highcharts distribution (v.3.0.5)

| 组件 | 描述 | | --- | --- | | annotations.js | 向图表元素添加注释的实用程序。 | | canvas-tools.js | 不支持 SVG 的 Android 2.x 设备的附加文件。它包含了用于 SVG 的 JavaScript 解析器canvg。 | | data.js | 一个实用程序,用于将输入源(如 CSV、HTML 表或网格视图)解析为基本配置选项,以便在 Highcharts 构造函数中直接使用。 | | exporting.js | 它允许用户下载 PDF、PNG、JPEG 或 SVG 矢量图像格式的图表。 | | funnel.js | 绘制漏斗图时需要。 | | Heatmap.js | 绘制热图时需要。 | | map.js | 绘制地理地图所必需的。 |

最后,你可以找到一个名为/js/themes的包含主题的目录,在表 18-2 中有描述。稍后将在单独的一节中对它们进行更详细的介绍。

表 18-2。

The themes in the Highcharts distribution (v.3.0.5)

| 主题 | 描述 | | --- | --- | | dark-blue.js | 它为图表添加了深蓝色背景。该系列的色彩鲜艳。 | | dark-green.js | 它为图表添加了深绿色背景。该系列的色彩鲜艳。 | | gray.js | 它为图表添加了黑色背景。该系列的色彩鲜艳。 | | grid.js | 它强化了图表下方的网格。该系列的色彩鲜艳。 | | skies.js | 它添加了一个天空和一些云作为背景。这个系列的颜色都是深色的。 |

异同

本节探讨了 jqPlot 库(您现在已经很熟悉了)和新的 Highcharts 库之间的异同。您会发现许多共同点,既包括您定义期权结构的方式,也包括许多其他方面,如数据处理。

但是,为了更好地理解这两个库之间的相似之处和不同之处,您将研究一个多系列折线图,并且使用相同的数据集,您将生成两个表示(每个库一个)。因此,分析这两种情况,您将比较代码的结构和表示本身。

从你之前在 jqPlot 中看到的基本多系列折线图的例子开始(见清单 18-1)。

清单 18-1。ch18_01x.html

<script type="text/javascript" src="../src/jquery.min.js"></script>

<script type="text/javascript" src="../src/jquery.jqplot.min.js"></script>

<link rel="stylesheet" type="text/css" href="../src/jquery.jqplot.min.css" />

<script type="text/javascript">

$(document).ready(function(){

var series1 = [1, 2, 3, 2, 3, 4];

var series2 = [3, 4, 5, 6, 5, 7];

var series3 = [5, 6, 8, 9, 7, 9];

var series4 = [7, 8, 9, 11, 10, 11];

$.jqplot ('myChart', [series1, series2, series3, series4]);

});

</script>

您可以使用 Highcharts 库来构建它的实际等价物,如清单 18-2 所示。

清单 18-2。ch18_01a.html

<script type="text/javascript" src="../src/js/jquery-1.9.1.js"></script>

<script type="text/javascript" src="../src/js/highcharts.js"></script>

<script>

var series1 = [1, 2, 3, 2, 3, 4];

var series2 = [3, 4, 5, 6, 5, 7];

var series3 = [5, 6, 8, 9, 7, 9];

var series4 = [7, 8, 9, 11, 10, 11];

$(function () {

$('#myChart').highcharts({

chart: {

type: 'line'

},

series: [{ data: series1 },

{ data: series2 },

{ data: series3 },

{ data: series4 }]

});

});

</script>

语法上的相似之处非常明显。作为参数传递给highcharts()函数的结构与您在 jqPlot 中用来定义为options的结构非常相似。在 Highcharts 中,这个结构被定义为一个配置对象。没有什么能阻止你通过一个options变量在外部定义它,就像你用 jqPlot 做的那样,如清单 18-3 所示。

清单 18-3。ch18_01a.html

var options = {

chart: {

type: 'line'

},

series: [{ data: series1 },

{ data: series2 },

{ data: series3 },

{ data: series4 }]

}

$(function () {

$('#myChart').highcharts(options);

});

加载相同的数据,但是使用两个不同的库,你得到如图 18-1 所示的图表。

A978-1-4302-6290-9_18_Fig1_HTML.jpg

图 18-1。

Comparison between the two line charts generated with the jqPlot (left) and Highcharts (right) libraries

从图 18-1 可以看出两个图表的布局差别很大。此外,在其基本配置中,Highcharts 库提供了一个标题、一个图例和轴标签,尽管没有明确指定这些内容。此外,甚至工具提示也是活动的,事实上,如果您将鼠标移动到线的点上,工具提示会立即出现,报告与该点相关的信息。

A978-1-4302-6290-9_18_Fig2_HTML.jpg

图 18-2。

By default, a tooltip reports the x and y values of the point and the name of the series to which it belongs

此外,当页面加载图表时,它是用动画从左到右逐渐绘制的。默认情况下,还会添加一个图例,对应 jqPlot 的enhancedLegendRenderer插件生成的图例(参见第十章中的“处理图例”部分);事实上,图例中的项目是活动的,如果您单击它们,相应的系列将从图表中删除。此外,一个非常重要的细节,轴和网格的比例会自动更新。它们适应剩余器械包覆盖的新范围,以优化显示效果。这是一种渐进效果,默认情况下包含在内。

所以很明显,高图表的起点几乎是 jqPlot 的顶点。事实上,正如您将在后面看到的,这个库将进一步扩展您的能力,而不需要接触代码,或者编写额外的函数或 JavaScript 库。

从基本代码来看,和 jqPlot 中的options一样,Highcharts 中的配置对象是由很多组件组成的。在这些组件中,有一整套预先填充了默认值的属性。如果没有明确指定这些属性,则图表将使用默认值。该行为几乎与 jqPlot 的options相同。这为使用这些库的开发人员提供了相当大的优势。尽管这些库没有指定所有的属性,但是它们代表了最常见的图表类型。这极大地方便了开发人员的工作,尤其是在缺乏经验的时候。

对象配置中最常用的组件对象有:

  • chart—其中属性定义为布局、事件、动画、相对边距和绘图区域大小
  • series—指定与数据系列及其属性相对应的数组
  • x 轴和 y 轴-在这里定义轴和轴标签的属性
  • 标题和副标题—在此插入图表的标题和最终副标题
  • legend—指定图例的所有内容
  • tooltip—指定工具提示的所有内容

如您所见,所有这些组件都已经很熟悉了。因此,在本章中,您不会对它们进行更深入的研究。那些希望深入研究配置对象组件的人可以参考 Highcharts 库官方网站上的完整文档( http://api.highcharts.com/highcharts )。

带高点图的折线图

您首先考虑一个简单的折线图,以理解 jqPlot 和 Highcharts 库之间的相似之处和不同之处。正如对 jqPlot 库所做的那样,本节通过更详细地处理折线图的实现来介绍 Highcharts 库。

首先,您将通过添加更多元素来完成简单的折线图。稍后,您将看到定义输入数据的不同方式,特别是通过管理它们的类别和范围。因此,您将看到更多的图形方面,比如网格、工具提示和图例,以及如何定制这些元素。最后,您将了解这个库提供的主题,包括它们是什么以及如何使用它们。

完成折线图

您刚刚使用 Highcharts 创建的折线图是在没有在配置对象中定义任何属性的情况下生成的,它只是突出显示了默认情况下来自 Highcharts 库的所有内容。首先,您将定义一组完成图表的属性,然后您将添加更多属性,用一些 jqPlot 库并不总是提供的新特性来丰富图表。因此,作为第一步,将清单 18-4 中用粗体突出显示的属性添加到您的基本代码中。

清单 18-4。ch18_01b.html

<script type="text/javascript" src="../src/js/jquery-1.9.1.js"></script>

<script type="text/javascript" src="../src/js/highcharts.js"></script>

<script>

var series1 = [1, 2, 3, 2, 3, 4];

var series2 = [3, 4, 5, 6, 5, 7];

var series3 = [5, 6, 8, 9, 7, 9];

var series4 = [7, 8, 9, 11, 10, 11];

$(function () {

$('#myChart').highcharts({

chart: {

type: 'line',

marginTop: 60,

marginLeft: 60

},

title: {

text: 'Central Art Museum',

x: 10

},

subtitle: {

text: '27th April,2013',

x: 10

},

xAxis: {

title: {

text: 'Time',

x: 100

},

categories:

['9:00', '11:00', '13:00', '15:00', '17:00', '19:00'],

tickmarkPlacement: 'on'

},

yAxis: {

title: {

text: 'Visitors'

}

},

series: [{

data: series1,

name: 'Australia'

}, {

data: series2,

name: 'Belgium'

}, {

data: series3,

name: 'Canada'

}, {

data: series4,

name: 'Danmark'

}],

legend: {

layout: 'vertical',

align: 'right',

verticalAlign: 'top',

y: 60

}

});

});

</script>

<div id="myChart" style="width: 600px; height: 400px;"></div>

你会得到图 18-3 中的折线图。

A978-1-4302-6290-9_18_Fig3_HTML.jpg

图 18-3。

A complete line chart with the Highcharts library

处理输入数据的不同方式

通常,具有两个坐标(x,y)的输入数据可以用两种不同的方式处理。如果 x 的值的序列对于所有的序列都是相同的,如正在讨论的例子,最好是分别处理坐标。您为每个序列定义一个 y 值数组,以及一个包含所有 x 值的数组。y 数组被传递给尽可能多的数据属性,在series组件中,用一个字符串在name属性中指定它们的名称。相反,x 数组被传递给xAxis中的categories属性。在第二种方法中,直接定义包含 x 和 y 两个值的数组。由于这种选择比较费力,所以当系列在两个坐标上不共享相同的值时更好。然而,举例来说,如果你像清单 18-5 那样定义这些点,你会得到相同的结果。

清单 18-5。ch18_01c.html

var serie1 = [ [Date.UTC(2013, 3, 27, 9), 1],

[Date.UTC(2013, 3, 27, 11), 2],

[Date.UTC(2013, 3, 27, 13), 3],

[Date.UTC(2013, 3, 27, 15), 2],

[Date.UTC(2013, 3, 27, 17), 3],

[Date.UTC(2013, 3, 27, 19), 4] ]

var serie2 = [ [Date.UTC(2013, 3, 27, 9), 3],

[Date.UTC(2013, 3, 27, 11), 4],

[Date.UTC(2013, 3, 27, 13), 5],

[Date.UTC(2013, 3, 27, 15), 6],

[Date.UTC(2013, 3, 27, 17), 5],

[Date.UTC(2013, 3, 27, 19), 7] ];

var serie3 = [ [Date.UTC(2013, 3, 27, 9), 5],

[Date.UTC(2013, 3, 27, 11), 6],

[Date.UTC(2013, 3, 27, 13), 8],

[Date.UTC(2013, 3, 27, 15), 9],

[Date.UTC(2013, 3, 27, 17), 7],

[Date.UTC(2013, 3, 27, 19), 9] ];

var serie4 = [ [Date.UTC(2013, 3, 27, 9), 7],

[Date.UTC(2013, 3, 27, 11), 8],

[Date.UTC(2013, 3, 27, 13), 9],

[Date.UTC(2013, 3, 27, 15), 11],

[Date.UTC(2013, 3, 27, 17), 10],

[Date.UTC(2013, 3, 27, 19), 11] ];

$(function () {

$('#myChart').highcharts({

...

xAxis: {

title: {

text: 'Time',

x: 100

},

type: 'datetime',

//categories: ['9:00', '11:00', '13:00', '15:00', '17:00', '19:00'],

tickmarkPlacement: 'on'

},

...

});

});

当您必须处理datetime数据:Date.UTC()时,这个例子很好地介绍了在 Highcharts 中经常使用的一种方法。这是一个 JavaScript 函数,它给出了从 1970 年 1 月 1 日 0:00 到作为参数传递的日期之间的毫秒数。它可以接受的参数数量是可变的,这取决于日期应该有多精确:

Date.UTC( years, months, days, hours, minutes, seconds)

如果您只想指定一年中的某几天,只需编写前三个参数就足够了:

Date.UTC(2012, 4, 27)

如本例所示,如果您正在讨论小时数,那么您需要指定四个参数:

Date.UTC(2013, 3, 27, 11)

Note

月的范围是 0-11(不是 1-12),日的范围是 1-31 天(和往常一样),小时的范围是 0-23。这就是为什么在这个例子中四月用数字 3 来表示。

您已经看到了在 Highcharts 库中传递输入数据的两种方法。但是在下面的示例中,您将使用第三个选项,它允许您在 x 轴上定义一个数值范围,而无需定义任何类别数组(在我看来,这种选择更适合条形图,而不是折线图,但是现在处理这一点很重要,以便获得完整的情况)。事实上,在折线图中,x 轴上的值基于非常精确的刻度而增加。Highcharts 提供了一个名为plotOptions ,的组件,它允许您定义两个特定的属性:pointIntervalpointStart。使用pointInterval,您可以定义一个点和下一个点之间的间隔,使用pointStart,您可以定义 x 轴上刻度的起始值。因为您必须处理 x 轴上一天的小时数,所以您有一个数据类型datetime。因此,为了在不指定传递给categories的值的情况下在 x 轴上获得相同的结果,您可以向配置对象添加新的组件,如清单 18-6 所示。

清单 18-6。ch18_01d.html

plotOptions: {

line: {

pointInterval: 2 * 3600 * 1000,// h * m* s hour

pointStart: Date.UTC(2013, 3, 27, 9, 0, 0)

}

},

网格:高级管理

使 Highcharts 成为比 jqPlot 更高级的库的特性之一是对图表底层网格的管理。事实上,这个库为您提供了一些属性,允许您在同一个图表上交替使用不同类型的网格布局,从而创造出令人愉悦的创新效果。通过修改你一直在做的例子,你会看到这是如何可能的(见清单 18-7)。

清单 18-7。ch18_01e.html

xAxis: {

title: {

text: 'Time',

x: 100

},

type: 'datetime',

gridLineWidth: 1,

gridLineDashStyle: 'dot',

minPadding: 0.1,

maxPadding: 0.1,

tickInterval: 4 * 3600 * 1000

},

yAxis: {

title: {

text: 'Visitors'

},

tickInterval: 4,

gridLineColor: '#618661',

minorTickInterval: 2,

minorGridLineColor: '#618661',

minorGridLineDashStyle: 'dashdot',

alternateGridColor: {

linearGradient: {

x1: 0, y1: 1,

x2: 1, y2: 1

},

stops : [

[0, "#F8F8EE"],

[1, "#A2B9A6"]

],

},

lineWidth: 1,

lineColor: '#CACACA',

tickWidth: 2,

tickLength: 4,

tickColor: '#CACACA'

},

除了不再用直线连接点,而是用曲线(对应于你在 jqPlot 中用smooth得到的),你需要把图表的类型从line改为spline(见清单 18-8)。

清单 18-8。ch18_01e.html

chart: {

type: 'spline',

marginTop: 60,

marginLeft: 60

},

plotOptions: {

spline: {

pointInterval: 2 * 3600 * 1000,// h * m* s hour

pointStart: Date.UTC(2013, 3, 27, 9, 0, 0)

}

},

从这些变化中你会得到如图 18-4 所示的图表。

A978-1-4302-6290-9_18_Fig4_HTML.jpg

图 18-4。

A grid with alternating colors, a dash style, and a gradient effect

网格线有两种不同的属性,以grid--minorGrid, and they开始,可以分别在 x 轴和 y 轴上定义。这种分成两个不同类的方法允许您为每个轴定义两种不同类型的网格,这两种网格将在它们之间逐行交替,从而给出一个更丰富、更多样化的图表。

此外,对于在网格线之间定义的区域,可以应用交替的颜色,甚至是可以调整方向的渐变。这都要归功于alternateGridColor属性,它包含两个要定义的附加属性。使用linearGradient可以给渐变一个方向——0 和 1 指定 x 和 y 上渐变的两个极端。使用stops,可以定义分配给 0 和 1 的颜色。

用 HTML 自定义工具提示

jqPlot 中什么构成了真正的定制,这里通过使用在tooltip组件中定义的四个属性来形式化。useHTML,如果设置为true,通过 HTML 标签激活新工具提示结构的构造模式。使用headerFormatpointFormatfooterFormat属性,您可以分别定义工具提示的头部、主体和尾部。从一点到另一点变化的动态值可以通过将它们包含在 HTML 结构的大括号中来访问。使用{ series.color }{ series.name },您可以访问颜色和系列的名称,而使用{ point.x }{ point.y },您可以访问点的 x 和 y 值(见清单 18-9)。

清单 18-9。ch18_01e.html

tooltip: {

useHTML: true,

headerFormat: '<small>{point.key}</small><table>',

pointFormat: '<tr><td style="color: {series.color}">' +

'<img src="flags/{series.name}.png" '+

'height="22" width="32"> {series.name}:</td>' +

'<td style="text-align: right"><b>{point.y}</b></td></tr>',

footerFormat: '</table>'

},

Note

在工具提示中显示标志所需的 PNG 文件包含在源代码中,该源代码可在该书的 press 产品页面( www.apress.com/9781430262893 )的“源代码/下载”选项卡上找到。

18-5 显示了您刚才所做的结果。

A978-1-4302-6290-9_18_Fig5a_HTML.jpgA978-1-4302-6290-9_18_Fig5b_HTML.jpgA978-1-4302-6290-9_18_Fig5c_HTML.jpgT3】

图 18-5。

With the HTML customization of the tooltips, it is possible to modify their default layout

用 HTML 自定义图例

同样类型的定制也适用于图例,如图 18-6 所示。

A978-1-4302-6290-9_18_Fig6_HTML.jpg

图 18-6。

A legend, customized using HTML with images and colors

这里也有必要启用useHTML属性,然后编写一个函数,将包含 HTML 代码的字符串返回给labelFormatter属性(见清单 18-10)。

清单 18-10。ch18_01f.html

legend: {

layout: 'vertical',

align: 'right',

verticalAlign: 'top',

y: 60,

useHTML: true,

labelFormatter: function () {

return '<table><tr><td style="color: '+this.color +

'"><img src="flags/'+ this.name +

'.png" height="15" width="20"><b> ' +

this.name;+'</b></td></tr></table>'

}

},

添加波段

色带可用于突出显示或定义图表中的区域。该功能完全集成在plotBands组件的 Highcharts 中,并且可以用很少的几行代码实现。该组件接受一个波段数组,您可以在每个波段中定义它的所有属性。根据定义plotBands的位置——在 x 轴或 y 轴上——可以得到水平或垂直条带。fromto属性定义了波段的界限。使用color属性,你可以决定用哪种颜色来绘制它。此外,借助label属性,您可以在每个区带中添加文本引用。

这个例子展示了垂直带的使用,所以你需要在 x 轴上添加plotBands组件,如清单 18-11 所示。

清单 18-11。ch18_01g.html

xAxis: {

...

plotBands: [{

from: Date.UTC(2013, 3, 27, 8, 0, 0),

to: Date.UTC(2013, 3, 27, 9, 0, 0),

color: '#CACACA',

label: {

text: 'Close',

style: {

color: '#000000'

}

}

},{

from: Date.UTC(2013, 3, 27, 19, 0, 0),

to: Date.UTC(2013, 3, 27, 20, 0, 0),

color: '#CACACA',

label: {

text: 'Close',

style: {

color: '#000000'

}

}

},{

from: Date.UTC(2013, 3, 27, 12, 30, 0),

to: Date.UTC(2013, 3, 27, 14, 0, 0),

color: '#FFE7B6',

label: {

text: 'Lunch',

style: {

color: '#000000'

}

}

}],

...

},

通过删除yAxis组件中的alternateGridColor属性及其所有内容,确保删除不再需要的交替区域网格的设置。清单 18-12 中加粗的代码行表明了这些变化。

清单 18-12。ch18_01g.html

yAxis: {

// You have to delete the following rows

alternateGridColor: {

linearGradient: {

x1: 0, y1: 1,

x2: 1, y2: 1

},

stops : [

[0, "#F8F8EE"],

[1, "#A2B9A6"]

],

},

//

},

此外,您可以进行进一步的定制。如果不喜欢默认颜色,可以用 jqPlot 中的颜色序列来替换它们。你将它们直接分配给colors组件(见清单 18-13)。

清单 18-13。ch18_01g.html

$('#myChart').highcharts({

colors: ["#4bb2c5", "#c5b47f", "#EAA228", "#579575"],

chart: {

...

18-7 显示了结果。

A978-1-4302-6290-9_18_Fig7_HTML.jpg

图 18-7。

Delimiting some areas of the chart with colored bands can enrich it with further information

自定标记点

对于那些习惯使用 jqPlot 的人来说,Highcharts 提供了许多额外的特性。您已经看到了如何定制图例和工具提示。标志点是什么?你甚至可以影响这些图表组件上的许多操作。例如,假设您想要显示一个图标来代替其中一个标记点,以便第一眼就突出显示图形上该给定点的特定特征(参见代码清单后面的注释)。为了做到这一点,你可以直接在输入数组中指定适当的属性(见清单 18-14),精确地在你想要标记点的地方。

清单 18-14。ch18_01g.html

var series1 = [1, 2, 3, {y:2, marker:{ symbol:'url(icon/info.png)'}}, 3, 4];

var series2 = [3, 4, 5, 6, 5, 7];

var series3 = [5, 6, 8, 9, 7, 9];

var series4 = [7, 8, 9, 11, 10, 11];

Note

您可以在本书随附的源代码中找到图标图像info.png,或者您也可以从 OpenIcon Library 网站的以下 URL 下载类似的图标: http://openiconlibrary.sourceforge.net 。这个网站包含了大量可供下载的图标。

输入数组中的每个值都可以被视为一个组件对象,由此可以指定所有属性,从而覆盖默认值。这种方法允许您在单个数据点级别区分行为和布局。

事实上,这将代替标记点出现(见图 18-8 )。

A978-1-4302-6290-9_18_Fig8_HTML.jpg

图 18-8。

Any icon can replace the original marker point, giving further info about a specific point in a series

排行榜的主题

在 jqPlot 中,您已经看到了通过定义级联样式表(CSS)类的属性来管理各种组件的样式,但是您还没有看到实际的主题。您可以考虑一个主题,比如 web 页面的各种组成元素上的一组特定的样式。这些配置存储在特殊文件中,以便可以重复使用。他们通常会创造出易于识别的个性化风格。这些配置通常被称为主题。

在其发行版中,Highcharts 提供了一些主题,您可以使用这些主题来描述您的图表:

  • 格子
  • 天空
  • 灰色的
  • 深蓝
  • 深绿色

要在网站上设置这些主题,必须包含显示相同样式名称的 JS 文件。对于本地方法,请使用以下内容:

<script src="../src/js/themes/dark-blue.js" type="text/javascript"></script>

或者,如果您喜欢使用 CDN 服务:

<script src="http://code.highcharts.com/themes/dark-blue.jsT2】

如图 18-9 所示,这些主题对你的图表布局的影响是显著的。

A978-1-4302-6290-9_18_Fig9_HTML.jpg

图 18-9。

In selecting a theme, you can accentuate certain aspects of your chart and increase the brightness of some colors

从文件中读取数据

通常,使用 Highcharts 或类似库的人需要读取文件中包含的数据。这一点非常重要,因为您的 web 页面及其 JavaScript 代码可以被视为一个真正的应用,它读取其他地方生成的数据,并且每次浏览器调用该页面时,这些数据都会发生变化。

生成数据的应用将数据写入可从网络访问的文件,如 CSV 文件。当用户想要查看图表中的数据时,浏览器会调用您编写 Highcharts 代码的 HTML 页面。每次都是 HTML 页面读取 CSV 文件(甚至可能不在同一台计算机上)中包含的数据,因此用户总是可以看到更新的数据。开发人员不需要每次对不同的页面重新编程,将数据写入 CSV 文件的应用将独立完成这项工作。当有一个数据库而不是一个文件时,就需要采取进一步的措施来请求数据。

使用$读取 CSV 文件。get()

为了读取文件的内容,jQuery 库提供了一个非常有用的函数:$.get()

$.get("aFile.html", function( data ) { ... });

这个函数使用 HTTP GET请求从服务器加载文件,并允许您使用嵌套函数读取其内容。(有关更多信息,请参见位于 http://api.jquery.com/jQuery.get/ 的 jQuery API 参考)。)

为了说明文件读取的过程,CSV 文件是一个很好的选择,因为这种格式很常见,使用起来也很简单。清单 18-15 显示了读取包含数据列的简单 CSV 文件的过程。

清单 18-15。data_08a.csv

date,open,min,max,close,

08/08/2012,1.238485,1.2227,1.250245,1.2372,

08/09/2012,1.23721,1.21671,1.24873,1.229295,

08/10/2012,1.2293,1.21417,1.25168,1.228975,

08/12/2012,1.229075,1.21747,1.23921,1.22747,

08/13/2012,1.227505,1.21608,1.24737,1.23262,

08/14/2012,1.23262,1.22167,1.248555,1.232385,

08/15/2012,1.232385,1.21641,1.254355,1.228865,

08/16/2012,1.22887,1.215625,1.247305,1.23573,

08/17/2012,1.23574,1.21891,1.23824,1.2333,

08/19/2012,1.23522,1.22291,1.245275,1.23323,

08/20/2012,1.233215,1.21954,1.256885,1.2351,

08/21/2012,1.23513,1.21465,1.258785,1.247655,

08/22/2012,1.247655,1.22315,1.264415,1.25338,

08/23/2012,1.25339,1.232465,1.288965,1.255995,

08/24/2012,1.255995,1.228175,1.276665,1.2512,

08/26/2012,1.25133,1.23042,1.292415,1.25054,

08/27/2012,1.25058,1.239025,1.28356,1.25012,

08/28/2012,1.250115,1.22656,1.287695,1.2571,

08/29/2012,1.25709,1.221895,1.29736,1.253065,

08/30/2012,1.253075,1.218785,1.27639,1.25097,

08/31/2012,1.25096,1.239375,1.283785,1.25795,

09/02/2012,1.257195,1.226845,1.298705,1.257355,

09/03/2012,1.25734,1.22604,1.271095,1.258635,

09/04/2012,1.25865,1.23264,1.282795,1.25339,

09/05/2012,1.2534,1.230195,1.27245,1.26005,

09/06/2012,1.26006,1.246165,1.28513,1.26309,

09/07/2012,1.26309,1.232655,1.291765,1.281625,

09/09/2012,1.28096,1.24915,1.311295,1.279565,

09/10/2012,1.27957,1.24552,1.30036,1.27617,

09/11/2012,1.27617,1.2459,1.29712,1.28515,

09/12/2012,1.28516,1.241625,1.31368,1.290235,

09/13/2012,1.227505,1.20608,1.25737,1.23262,

09/14/2012,1.24262,1.22167,1.278555,1.232385,

09/15/2012,1.252385,1.21641,1.284355,1.228865,

09/16/2012,1.24887,1.225625,1.257305,1.23573,

09/17/2012,1.24574,1.22891,1.26824,1.2333,

09/19/2012,1.24522,1.23291,1.255275,1.23323,

09/20/2012,1.233215,1.21954,1.256885,1.2351,

09/21/2012,1.22513,1.21465,1.248785,1.247655,

09/22/2012,1.227655,1.21315,1.254415,1.25338,

09/23/2012,1.22339,1.202465,1.258965,1.255995,

09/24/2012,1.215995,1.208175,1.256665,1.2512,

09/26/2012,1.22133,1.20042,1.252415,1.25054,

09/27/2012,1.22058,1.209025,1.25356,1.25012,

09/28/2012,1.230115,1.21656,1.257695,1.2571,

09/29/2012,1.24709,1.221895,1.25736,1.253065,

09/30/2012,1.233075,1.218785,1.25639,1.25097,

09/31/2012,1.24096,1.229375,1.263785,1.25795,

10/02/2012,1.257195,1.226845,1.258705,1.257355,

10/03/2012,1.25734,1.22604,1.271095,1.258635,

10/04/2012,1.25865,1.23264,1.282795,1.25339,

10/05/2012,1.2534,1.210195,1.28245,1.26005,

10/06/2012,1.26006,1.226165,1.28513,1.26309,

10/07/2012,1.26309,1.232655,1.281765,1.281625,

10/09/2012,1.28096,1.24915,1.291295,1.279565,

10/10/2012,1.29957,1.25552,1.31036,1.27617,

10/11/2012,1.30617,1.2559,1.32712,1.28515,

10/12/2012,1.28516,1.261625,1.31368,1.290235,

该文件包含五列。第一个是一列datetime值,沿 x 轴标记时间,接下来的四个是 OHLC(开盘-盘高-盘低-收盘)数据类型。为了处理第一种类型的数据,您需要构建一个合适的解析器。这将包含一个regex表达式来捕获第一列中的日、月和年的值,以及一个函数来以代码可以管理的格式重构它们(见清单 18-16)。

清单 18-16。ch18_02a.html

Highcharts.Data.prototype.dateFormats['m/d/Y'] = {

regex: '^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})$',

parser: function (match) {

return Date.UTC(match[3], match[1] - 1, +match[2]);

}

};

现在,您必须确保 JavaScript 代码能够读取 CSV 文件中包含的数据。为此,您必须声明函数$.get(),并将文件名和扫描内容的函数作为参数传递。在$.get()函数中,你放置了highcharts()函数,这样它就可以通过csv变量看到文件中包含的所有数据(见清单 18-17)。

清单 18-17。ch18_02a.html

$.get('data_08a.csv', function (csv) {

$('#myChart').highcharts(options);

});

现在是时候定义图表各个组件的所有属性了。就像在 jqPlot 中一样,即使使用 Highcharts,当考虑越来越复杂的情况时,最好还是在模块中推理。因此,从现在开始,您将通过options变量在外部定义配置对象,然后将它作为参数传递给highcharts()函数(参见清单 18-18)。

清单 18-18。ch18_02a.html

$.get('data_08.csv', function (csv) {

var options = {

colors: ["#005B06", "#000000", "#9D3C27", "#000000"],

data: {

csv: csv

},

subtitle: {

text: 'Prices of the day',

style: {

color: '#005B06',

fontSize: '12px'

}

},

title: {

text: 'Spaghetti Lunghetti',

style: {

color: '#005B06',

fontSize: '16px'

}

},

xAxis: {

type: 'datetime',

tickInterval: 7 * 24 * 3600 * 1000, // one week

tickWidth: 0,

gridLineWidth: 1,

labels: {

align: 'left'

}

},

yAxis: {

title: {

text: 'Dollars ($)',

style: {

color: '#005B06',

fontSize: '12px'

}

}

},

plotOptions: {

area: {

fillColor: {

linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1},

stops: [ [0, '#00602F'], [1, '#FFFFFF']]

},

lineWidth: 1,

marker: {

enabled: false

},

shadow: false,

states: {

hover: { lineWidth: 1 }

},

threshold: null

},

},

series: [{

name: 'Open',

type: 'area',

lineWidth: 4,

marker: {

radius: 4

},

},{ visible: false},

{

name: 'Max',

type: 'spline',

lineWidth: 4,

marker: {

enabled: false

}

},{ visible: false}]

};

$('#myChart').highcharts(options);

});

要激活读取写入文件的数据,您需要包含data模块,您可以在发行版中找到它。对于本地方法,请使用以下内容:

<script src="../src/js/modules/data.js"></script>

或者,如果您喜欢使用 CDN 服务:

<script src="http://code.highcharts.com/modules/data.jsT2】

在浏览器中加载页面,你会得到图 18-10 中的图表。

A978-1-4302-6290-9_18_Fig10_HTML.jpg

图 18-10。

By default all the series in an imported CSV file are read; you can hide them if need be

从数据中排除 CSV 列

请注意,CSV 文件包含占据最后四列数据的 OHLC 值列表。假设在这种情况下,您只对显示那些包含openmax值的列感兴趣。你想忽略剩下的两个。如果查看series对象,还需要定义四个不同的数列,其中第二个和第四个隐藏在图表中(没有删除!).因此,minclose系列仍然保留在图例中。如果你点击它们,这些系列会出现在图表上,很多时候这是不可取的。

当您想只显示第一列而不显示最后一列时,您必须采用不同的方法(例如,如果您想只显示open,或openmin,或openminmax)。为此,您需要在series对象中分别指定第一列、前两列或前三列。但是你不能跳过一个系列直接考虑下一个。

因此,如果您打算查看文件中的所有系列,这种方法是最佳的,但有时情况并非如此。您经常会有包含大量列的文件,但希望只提取几列。然后,您需要遵循不同的方法,例如,输入一个 JavaScript 函数作为解析器,只提取您感兴趣的列。现在,您将看到如何修改前面的示例来实现这一点。

首先,在函数$.get()读取了 CSV 文件中包含的所有数据后,立即插入一个解析器。您定义了两个包含数据列openmax的数组,它们都与日期相关。随后通过逐行扫描,这两个数组将填充相应的值。这样,您排除了您不感兴趣的列,并且您可以只处理openmax变量,它们将被传递给series对象。清单 18-19 显示了这方面的代码。

清单 18-19。ch18_02b.html

$.get('data_08a.csv', function (csv) {

var open = [];

var max = [];

var lines = csv.split('\n');

$.each(lines, function(lineNo, line) {

var items = line.split(',');

if (lineNo != 0) {

open.push([items[0], parseFloat(items[1])]);

max.push([items[0], parseFloat(items[3])]);

}

});

...

options中的data对象(见清单 18-20)不再需要;因此,您应该删除它。

清单 18-20。ch18_02b.html

var options = {

colors: ["#005B06", "#9D3C27"],

//data: {

//csv: csv

//},

使用data属性将这两个数组直接传递给series对象(见清单 18-21)。

清单 18-21。ch18_02b.html

series: [{

data: open,

name: 'Open',

type: 'area',

lineWidth: 4,

marker: {

radius: 4

},

},{

data: max,

name: 'Max',

type: 'spline',

lineWidth: 4,

marker: {

enabled: false

}

}]

18-11 显示了不再引用不需要的系列的新图表。

A978-1-4302-6290-9_18_Fig11_HTML.jpg

图 18-11。

By adding a parser, it is possible to filter only for series of interest (look at the legend)

导出图表

Highcharts 提供的一个全新特性是用户可以从浏览器中导出各种格式的图表,包括 PNG 或 JPG 图像、SVG 矢量或 PDF 文档。甚至可以打印图像。所有这些都是通过使用图表右上角的小按钮访问的菜单来完成的(参见图 18-12 )。

A978-1-4302-6290-9_18_Fig12_HTML.jpg

图 18-12。

By clicking the icon in the upper-right corner of the chart, you’ll access a context menu that contains options for exporting the chart

只要您在网页中包含exporting模块,该按钮就会出现在您的图表中。您可以在 Highcharts 发行版中找到这个模块。对于本地方法,请使用以下内容:

<script src="../src/js/modules/exporting.js"></script>

或者,如果您喜欢使用 CDN 服务:

<script src="http://code.highcharts.com/modules/exporting.jsT2】

主详细信息图表

主详细信息图表就是一个折线图的例子,它恰当地说明了像 Highcharts 这样的库所能提供的功能。这种类型的折线图由同时显示的同一折线图的两种表示形式组成。目的是将焦点集中到折线图的特定部分,而不会丢失整个图表。

这两个图表称为主图表和详细图表。正如您在图 18-13 中看到的,主图表通常要小得多,并且被放置在相对于细节图表的边缘位置;它通常显示在底部,其 x 轴与详细信息图表对齐,以提供透视感。详细信息图表位于前台,以便用户可以关注它。详细图表更详细地显示主图表的选定区域。

A978-1-4302-6290-9_18_Fig13_HTML.jpg

图 18-13。

A master and detail chart

当您必须处理大量难以完整查看的数据时,主详细信息图表是一个不错的选择。这些数据通常被写入从其他应用获取的外部文件中。继续前面的意大利面条价格示例,您将从一个 CSV 文件构建一个主细节图表。

在开始真正的例子之前,您需要在本地方法中包含库文件和深绿色主题:

<script type="text/javascript" src="../src/js/jquery-1.9.1.js"></script>

<script type="text/javascript" src="../src/js/highcharts.js"></script>

<script type="text/javascript" src="../src/js/modules/data.js"></script>

<script type="text/javascript" src="../src/js/themes/dark-green.js"></script>

或者,如果您喜欢使用 CDN 服务:

<script src="http://code.jquery.com/jquery-1.9.1.min.jsT2】

<script src="http://code.highcharts.com/highcharts.jsT2】

<script src="http://code.highcharts.com/modules/data.jsT2】

<script src="http://code.highcharts.com/themes/dark-green.jsT2】

因为在这个例子中,你将使用与前一个例子相同的 CSV 文件(见清单 18-15),你需要一个合适的解析器来正确地读取数据。这个解析器将包含一个regex表达式来捕获日、月和年的值。清单 18-22 展示了您之前重新开发的解析器(见清单 18-16),因为它仍然有效。

清单 18-22。ch18_03.html

<script>

$(function () {

Highcharts.Data.prototype.dateFormats['m/d/Y'] = {

regex: '^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})$',

parser: function (match) {

return Date.UTC(match[3], match[1] - 1, +match[2]);

}

};

});

</script>

...

<div id="myChart" style="width: 600px; height: 400px;"></div>

别忘了添加一个以myChartid<div>元素。

主细节图由两个折线图组成,您可以通过两个变量来定义它们:masterChartdetailChart(参见清单 18-23)。这两张图表将绘制在两个不同的区域,master-containerdetail-container。多亏了 jQuery,您将使用这些标识符创建两个<div>元素。这两个图表将由两个 JavaScript 函数创建,createDetail()createMaster()。甚至在控制两个图表的布局和行为的两个配置对象的定义中也保持了这种二元性:detailOptionsmasterOptions

清单 18-23。ch18_03.html

$.get('data_08a.csv', function (csv) {

var masterChart,detailChart;

function createMaster() {

masterChart = $('#master-container')

.highcharts(masterOptions, function(masterChart) {

createDetail(masterChart);

}).highcharts(); // return chart instance

}

function createDetail(masterChart) {

var detailData = [],

detailStart = Date.UTC(2012, 7, 1);

jQuery.each(masterChart.series[0].data, function(i, point) {

if (point.x >= detailStart) {

detailData.push(point.y);

}

});

detailChart = $('#detail-container').highcharts(detailOptions)

.highcharts();

}

var $container = $('#myChart')

.css('position', 'relative');

var $detailContainer = $('<div id="detail-container">')

.appendTo($container);

var $masterContainer = $('<div id="master-container">')

.css({ position: 'absolute', top: 300, height: 80, width: '100%'})

.appendTo($container);

createMaster();

});

每次重新绘制图表时,首先调用createDetail()函数。它会创建相应的图表。在函数内部,调用了createMaster()函数,该函数基于第一个图表中的选定区域创建第二个图表。

现在,您必须定义两个配置对象。在清单 18-24 中,您定义了关于主图表的选项。当查看masterOptions对象时,请将注意力集中在chart对象中events属性的定义上。如您所见,在selection属性中定义了一个通用函数。事实上,在主图表中,您需要用鼠标选择一个特定的范围。因此,该图表部分将显示在详细图表中。通过这个函数实现的恰恰是这个特性。extremesObject变量存储所选区域覆盖的 x 轴的间隔,并将其传递给event.xAxis[0]值。因此,您提取这个范围的最大值和最小值(minmax),这正是您需要的极端值。然后,这些值既用于绘制详细图表中的点,也用于绘制主图表中的阴影区域。事实上,下一步是使用一个each()函数来选择minmax值之间的数据。所有这些数据点都存储在detailData数组中。该数组将成为详细信息图表的输入数据数组。

清单 18-24。ch18_03.html

var masterOptions = {

colors: ['#FFE76D'],

data: {

csv: csv

},

chart: {

reflow: false,

borderWidth: 0,

backgroundColor: null,

marginLeft: 50,

marginRight: 20,

zoomType: 'x',

events: {

selection: function(event) {

var extremesObject = event.xAxis[0],

min = extremesObject.min,

max = extremesObject.max,

detailData = [],

xAxis = this.xAxis[0];

jQuery.each(this.series[0].data, function(i, point) {

if (point.x > min && point.x < max) {

detailData.push({

x: point.x,

y: point.y

});

}

});

xAxis.removePlotBand('mask-before');

xAxis.addPlotBand({

id: 'mask-before',

from: Date.UTC(2012, 1, 1),

to: min,

color: 'rgba(1, 1, 1, 0.5)'

});

xAxis.removePlotBand('mask-after');

xAxis.addPlotBand({

id: 'mask-after',

from: max,

to: Date.UTC(2013, 1, 1),

color: 'rgba(1, 1, 1, 0.5)'

});

detailChart.series[0].setData(detailData);

return false;

}

}

},

title: {

text: null

},

xAxis: {

type: 'datetime',

showLastTickLabel: true,

title: {

text: null

}

},

yAxis: {

gridLineWidth: 0,

labels: {

enabled: false

},

title: {

text: null

},

min: 1.25,

max: 1.31

},

tooltip: {

formatter: function() {

return false;

}

},

legend: {

enabled: false

},

credits: {

enabled: false

},

plotOptions: {

series: {

fillColor: {

linearGradient: [0, 0, 0, 70],

stops: [

[0, '#FFE76D'],

[1, 'rgba(0, 0, 0, 0)']

]

},

lineWidth: 1,

marker: {

enabled: false

},

shadow: false,

states: {

hover: {

lineWidth: 1

}

},

enableMouseTracking: false

}

},

series: [{

name: 'open',

type: 'area',

lineWidth: 2

}],

exporting: {

enabled: false

}

};

minmax值的另一个用途是界定主图表中的选定区域。您可以通过添加两个波段并用mask-beforemask-afterid 标识它们来实现这一点。这些带是用来给没有被选中的区域加阴影的。因此,第一个波段将从实验数据的最低可能值开始(超出主图表的左边缘)。因此,例如,您将值Date.UTC (2012,1,1)赋给from属性,波段将对应于赋给to属性的min值。同样的事情也适用于第二个波段。它从分配给from属性的max值开始,以实验数据的最高可能值结束(超出主图表的右边缘)。您可以将Date.UTC(2013,1,1)分配给to属性。

在清单 18-25 中,您定义了细节图表的选项。如您所见,这个定义与折线图的定义没有太大的不同。

清单 18-25。ch18_03.html

var detailOptions = {

colors: ["#FFE76D"],

data: {

csv: csv

},

chart: {

marginBottom: 120,

reflow: false,

marginLeft: 50,

marginRight: 20,

style: {

position: 'absolute'

}

},

credits: {

enabled: false

},

title: {

text: 'Spaghetti Lunghetti',

style:{

color: '#FFE76D',

fontSize: '16px'

}

},

xAxis: {

type: 'datetime'

},

yAxis: {

title: {

text: null

},

maxZoom: 0.1

},

tooltip: {

formatter: function() {

var point = this.points[0];

return Highcharts.dateFormat('%A %B %e %Y', this.x) + ':<br/><b>'+

Highcharts.numberFormat(point.y, 2) +' USD</b>';

},

shared: true

},

legend: {

enabled: false

},

plotOptions: {

series: {

marker: {

enabled: false,

states: {

hover: {

enabled: true,

radius: 3

}

}

}

}

},

series: [{

name: 'Open',

lineWidth: 4

}],

exporting: {

enabled: false

}

};

最后,这里是主细节图(见图 18-14 )。

A978-1-4302-6290-9_18_Fig14_HTML.jpg

图 18-14。

The master chart is on the bottom and the detail chart is in the foreground

从主图表中,你可以用鼠标选择你感兴趣的部分,你将能够看到它的细节。

带高图表的条形图和饼图

在本节中,您将研究三个简短的示例,展示 Highcharts 如何实现常见的条形图和饼图。

事实上,你会看到实现一个条形图是多么简单。借助一些示例,您将看到如何更轻松地在分组模式和堆叠模式之间进行切换,从水平模式切换到垂直模式。甚至实现一个饼图也是非常简单和直观的。这一点很重要,因为正如您将在随后的章节中看到的,条形图和饼图本身被用作更复杂图表类型的组件。

条形图

由于条形图易于实现,您将立即从堆叠的垂直条形图开始。首先,记得包含库文件和一个主题,比如这里包含的深绿色主题:

<script type="text/javascript" src="../src/js/jquery-1.9.1.js"></script>

<script type="text/javascript" src="../src/js/highcharts.js"></script>

<script type="text/javascript" src="../src/js/themes/dark-green.js"></script>

或者,如果您喜欢使用 CDN 服务:

<script type="text/javascript" src="http://code.jquery.comT2】

T0T1】

<script type="text/javascript"

src="http://code.highcharts.com/3.0.5/highcharts.jsT2】

<script type="text/javascript" src="http://code.highcharts.com/themesT2】

T0T1】

您将看到,只需更改一个属性,就可以得到各种条形图。然后,为了创建一个带有竖条的条形图,有必要将type属性指定为column(见清单 18-26)。

清单 18-26。ch18_04a.html

$(function () {

var data1 = [46.6, 14.8, 0, 61.6];

var data2 = [2.6, 13.8, 72.6, 9.1];

var data3 = [3.3, 53.5, 77.1, 10.6];

var options = {

chart: {

type: 'column'

},

title: {

text: 'Nutrition label'

},

xAxis: {

categories: ['Carrots', 'Beans', 'Chicken', 'Bread']

},

yAxis: {

min: 0,

title: {

text: 'Calories'

}

},

legend: {

reversed: true

},

plotOptions: {

series: {

stacking: 'normal'

}

},

series: [{

name: 'Carbohydrate',

data: data1

},{

name: 'Fat',

data: data2

},{

name: 'Protein',

data: data3

}]

}

$('#myChart').highcharts(options);

});

18-15 显示了我们的图表。

A978-1-4302-6290-9_18_Fig15_HTML.jpg

图 18-15。

A simple stacked bar chart

如果你想要一个水平堆积条形图,你必须将type属性定义为bar,如清单 18-27 所示。

清单 18-27。ch18_04b.html

chart: {

type: 'bar'

},

这是从垂直转换为水平的图表(见图 18-16 )。

A978-1-4302-6290-9_18_Fig16_HTML.jpg

图 18-16。

A simple horizontal stacked bar chart

如果你想要一个普通的分组条形图,你只需通过options删除plotOptions对象,如清单 18-28 所示。

清单 18-28。ch18_04c.html

//Delete the plotOptions object

plotOptions: {

series: {

stacking: 'normal'

}

},

您将得到两个分组条形图,如图 18-17 所示。

A978-1-4302-6290-9_18_Fig17_HTML.jpg

图 18-17。

The previous bar charts in the grouped version

饼图

用 Highcharts 库构建一个饼图也非常简单(见清单 18-29)。Highcharts 接受格式为[label,value]的传入数据,然后将值转换为百分比,并根据百分比以正确的比例创建切片。

清单 18-29。ch18_04d.html

$(function () {

var data2 = [['Analysis', 5], ['Designing', 10], ['Developing', 20],

['Deploying', 5], ['Test', 28], ['Debugging', 23], ['Sale', 9]];

//Add options here

$('#myChart').highcharts(options);

});

看看在plotOptions对象中的pie对象中指定的属性。通过将allowPointSelect属性设置为true并将cursor属性设置为pointer(参见清单 18-30),您可以管理特定的事件。当用户点击一个切片时,它被从蛋糕中提取出来并到达在slicedOffset属性中指定的距离。

清单 18-30。ch18_04d.html

var options = {

colors: ['#65Af43', '#FFE76D', '#BB43F2',

'#A50f33', '#15CACA', '#612BF3', '#FF8E04'],

chart: {

},

title: {

text: 'Developing of the X Weapon'

},

yAxis: {

min: 0,

title: {

text: 'Calories'

}

},

tooltip: {

pointFormat: '{series.name}: <b>{point.y}%</b>',

},

plotOptions: {

pie: {

allowPointSelect: true,

cursor: 'pointer',

showInLegend: true,

slicedOffset: 20

}

},

series: [{

type: 'pie',

name: 'Budget',

data: data2,

borderColor: '#888888',

borderWidth: 1,

dataLabels: {

enabled: true,

color: '#bbbbbb',

connectorColor: '#bbbbbb',

format: '{point.name}: <b>{point.y}%</div></b>'

},

}]

}

18-18 显示了使用深绿色主题的饼图。

A978-1-4302-6290-9_18_Fig18_HTML.jpg

图 18-18。

A pie chart with all elements included

甘特图

另一种可以使用 Highcharts 轻松创建的图表是甘特图(见图 18-19 )。这是一种条形图,由亨利·甘特于 1910 年开发,用于项目进度安排。最近,它被用来表示活动之间的关系。例如,在关于 Java 或 C++高级编程的书籍中,您通常会发现这样的图表表示各种函数调用之间的同步性或非同步性。

A978-1-4302-6290-9_18_Fig19_HTML.jpg

图 18-19。

A Gantt chart representing the development process of a project

要实现这种类型的图表,您必须包括扩展模块highcharts-more.js。这个扩展包含了最近开发的许多类型的组件,包括columrange类型,它允许您实现条形图的经典条,但是不同之处在于定义了最大值(高)和最小值(低)。因此,用水平方向(x 轴)和垂直方向(y 轴)来表示一个范围是很有用的。甘特图在 x 轴上描述时间间隔,而在 y 轴上需要分组分类。这种分类可以通过向categories属性传递一个名称数组来轻松完成。

在下面的例子中,您将使用灰色主题。在实现图表之前,您需要在网页上包含这两个文件:

<script type="text/javascript" src="../src/js/highcharts-more.js"></script>

<script type="text/javascript” src="../src/js/themes/gray.js"></script>

或者,如果您喜欢使用 CDN 服务:

<script src="http://code.highcharts.com/highcharts-more.jsT2】

<script src="http://code.highcharts.com/themes/gray.jsT2】

请记住,甘特图表示时间范围,因此您可以通过每个元素有两个值的数组来定义时间范围:[低,高]。因为它们是datetime值,建议选择使用函数Date.UTC()来定义这些时间间隔(见清单 18-31)。

清单 18-31。ch18_05.html

var data1 = [ [Date.UTC(2012, 0, 1), Date.UTC(2012, 1, 15)],

[Date.UTC(2012, 0, 20), Date.UTC(2012, 1, 28)],

[Date.UTC(2012, 1, 4), Date.UTC(2012, 3, 30)],

[Date.UTC(2012, 2, 10), Date.UTC(2012, 5, 15)],

[Date.UTC(2012, 4, 1), Date.UTC(2012, 7, 19)],

[Date.UTC(2012, 6, 1), Date.UTC(2012, 10, 15)],

[Date.UTC(2012, 9, 1), Date.UTC(2012, 11, 28)];

现在你用options变量定义配置对象(见清单 18-32)。在chart组件对象中,需要将inverted属性设置为true。这是因为您希望条是水平的。但是,你必须小心,因为这样做的话,你就颠倒了轴。垂直轴是 x 轴,水平轴是 y 轴。当您定义每个属性时,尤其是当您需要弄清楚如何正确输入数据时,您需要考虑这一点。

清单 18-32。ch18_05.html

var options = {

chart: {

type: 'columnrange',

inverted: true

},

这种颠倒的第一个后果是,您必须在xAxis对象中输入类别的名称,以便它们出现在垂直轴上。在图表中输入一个标题和副标题后,在categories属性中插入一个字符串数组,包含分配给每个时间间隔的名称(见清单 18-33)。

清单 18-33。ch18_05.html

title: {

text: 'Developing of the X Weapon'

},

subtitle: {

text: 'Half Guns inc.'

},

xAxis: {

categories: ['Analysis', 'Designing', 'Developing',

'Deploying', 'Test', 'Debugging', 'Sale']

},

...

另一方面,您必须将 y 轴视为时间轴,并相应地定义网格。使用title属性定义轴标签,并通过在type属性中指定datetime来定义轴必须管理的数据类型。然后你必须将minPaddingmaxPadding属性设置为0。这将迫使图表用输入数据中定义的时间间隔填充整个 x 轴(见清单 18-34)。

清单 18-34。ch18_05.html

yAxis: {

title: {

text: 'Scheduling'

},

type: 'datetime',

minPadding: 0,

maxPadding: 0,

gridLineWidth: 1,

gridLineColor: '#bbbbbb',

minorTickInterval: 14 * 24 * 3600000 //2 week

},

...

您已经告诉 Highcharts 库通过chart对象中的type属性将数据解释为columnrange。现在是时候定义它的属性了,你可以在series对象中定义,也可以在plotOptions对象下的columnrange对象中定义(见清单 18-35)。除非另外指定,否则您将拥有相同颜色的所有条形,因为它们属于同一系列,但是因为您想要相反的颜色,所以您必须用true激活colorByPoint属性,然后将所需的颜色序列指定为colors属性的数组。最后,在禁用图例和工具提示之后,将数据传递给series对象中的data属性。

清单 18-35。ch18_05.html

...

plotOptions:{

columnrange:{

colorByPoint: true,

colors:['#65Af43','#FFE76D','#BB43F2','#A50f33',

'#15CACA','#612BF3','#FF8E04']

}

},

legend: {

enabled: false

},

tooltip: {

enabled: false

},

series: [{

data: data1,

borderColor: 'black',

borderWidth: 2

}]

}

$('#myChart').highcharts(options);

最后,这是我们的甘特图(见图 18-20 )。

A978-1-4302-6290-9_18_Fig20_HTML.jpg

图 18-20。

A Gantt chart

组合图表

或许 Highcharts 库与众不同的一面是,它能够在同一个图表中重叠几种类型的图表,从而创建真正壮观的表示。此外,该库的结构使得叠加图表的配置值保持不同,因此易于管理。您可以分别开发它们,然后最终将它们组合在一起。

在下一个示例中,您将连接已经实现的饼图和甘特图,并添加第三个图表,即折线图。首先,导入所有必要的文件,包括灰色主题:

<script type="text/javascript" src="../src/js/jquery-1.9.1.js"></script>

<script type="text/javascript" src="../src/js/highcharts.js"></script>

<script type="text/javascript" src="../src/js/highcharts-more.js"></script>

<script type="text/javascript" src="../src/js/themes/gray.js"></script>

或者,如果您喜欢使用 CDN 服务:

<script src="http://code.highcharts.com/jquery-1.9.1.jsT2】

<script src="http://code.highcharts.com/highcharts.jsT2】

<script src="http://code.highcharts.com/highcharts-more.jsT2】

<script src="http://code.highcharts.com/themes/gray.jsT2】

您需要定义将用于所有三个图表的数据(见清单 18-36)。在data1,中,输入定义甘特图间隔的数据【低,高】。在data2,中,您输入饼图的数据。注意,为了方便起见,这些值已经表示为百分比。在data3中,您输入代表折线图各点的[x,y]数据(此处您将datetime值定义为 y 值,将数值定义为 x 值,因为您已经反转了轴)。最后,在gradient变量中添加颜色渐变的定义,以避免在配置对象中多次写入。

清单 18-36。ch18_06.html

var data1 = [[Date.UTC(2012, 0, 1), Date.UTC(2012, 1, 15)],

[Date.UTC(2012, 0, 20), Date.UTC(2012, 1, 28)],

[Date.UTC(2012, 1, 4), Date.UTC(2012, 3, 30)],

[Date.UTC(2012, 2, 10), Date.UTC(2012, 5, 15)],

[Date.UTC(2012, 4, 1), Date.UTC(2012, 7, 19)],

[Date.UTC(2012, 6, 1), Date.UTC(2012, 10, 15)],

[Date.UTC(2012, 9, 1), Date.UTC(2012, 11, 28)]];

var data2 = [5, 10, 20, 5, 28, 23, 9];

var data3 = [[140, Date.UTC(2012,0,1)],

[120, Date.UTC(2012, 1, 28)],

[58, Date.UTC(2012, 3, 30)],

[78, Date.UTC(2012, 5, 15)],

[44, Date.UTC(2012, 7, 19)],

[33, Date.UTC(2012, 10, 15)],

[1, Date.UTC(2012, 11, 28)]];

var gradient = {x1:0, y1:0, x2:0, y2:1};

现在可以开始定义配置对象了(见清单 18-37)。因为上一个例子中的颜色序列效果很好,所以您将在这个例子中再次使用它。然后使用true激活inverted属性来反转 x 轴和 y 轴。最后,给你的图表添加一个标题和副标题。

清单 18-37。ch18_06.html

var options = {

colors:['#65Af43', '#FFE76D', '#BB43F2',

'#A50f33', '#15CACA', '#612BF3', '#FF8E04'],

chart: {

inverted: true

},

title: {

text: 'Developing of the X Weapon'

},

subtitle:{

text: 'Half Guns inc.'

},

对于组合图,你需要在 x 轴上定义两个不同的刻度——一个包含上例中的类别,另一个是线性类型,代表线性图(见清单 18-38)。关于 y 轴,您必须定义一个时间刻度,并且还要管理网格。y 轴上刻度后面的主网格扫描月份,而您将设置次网格扫描两周的时间段。

清单 18-38。ch18_06.html

xAxis:[ {

categories: ['Analysis', 'Designing', 'Casting',

'Develop', 'Test', 'Debugging', 'Sale']

},{

title: {

text: 'Budget'

},

labels: {

enabled: false

},

opposite: true,

tickInterval: 20

}],

yAxis: {

title: {

text: 'Scheduling'

},

type: 'datetime',

minPadding: 0,

maxPadding: 0,

gridLineWidth: 1,

gridLineColor: '#bbbbbb',

minorTickInterval: 14 * 24 * 3600000 //2 week

},

对于甘特图,你需要对之前的版本做一些修改(见清单 18-39)。将颜色渐变应用到条形和区域。在这里,您可以使用渐变透明度来创建动态外观。

清单 18-39。ch18_06.html

plotOptions:{

columnrange:{

colorByPoint: true,

colors: [{

linearGradient: gradient,

stops: [[0, 'rgba(101, 175, 67, 1)'],

[1, 'rgba(101, 175, 67, 0)']]

},{

linearGradient: gradient,

stops: [[0, 'rgba(255, 231, 109, 1)'],

[1, 'rgba(255, 231, 109, 0)']]

},{

linearGradient: gradient,

stops: [[0, 'rgba(187, 67, 242, 1)'],

[1, 'rgba(187, 67, 242, 0)']]

},{

linearGradient: gradient,

stops: [[0, 'rgba(165, 15, 51, 1)'],

[1, 'rgba(165, 15, 51, 0)']]

},{

linearGradient: gradient,

stops: [[0, 'rgba(21, 202, 202, 1)'],

[1, 'rgba(21, 202, 202, 0)']]

},{

linearGradient: gradient,

stops: [[0, 'rgba(97, 43, 243, 1)'],

[1,'rgba(97, 43, 243, 0)']]

},{

linearGradient: gradient,

stops: [[0, 'rgba(255, 142, 4, 1)'],

[1, 'rgba(255, 142, 4, 0)']]

}]

}

},

您还可以禁用图例和工具提示,并开始输入三种图表类型的属性。在series对象中,您定义了三个不同的对象,每个对象对应一个图表。当您使用组合图表时,您必须记住它们的绘制顺序。事实上,在series对象中定义的每个图表都是在前一个图表之上绘制的。如果有重叠的部分,它们将被覆盖。所以在series对象中定义图表的顺序很重要。

首先,定义折线图,以使其成为背景。在本例中,您还可以修改标记的形状、大小和颜色。然后定义甘特图,记住将值0添加到属性xAxis中。这意味着甘特图中的数据,即data1,被传递到第一个 x 轴。最后,定义饼图的属性,使用center属性可以将饼图移动到不与其他图表重叠的位置(见清单 18-40)。使用size属性,您可以减小馅饼的大小,以便更好地适应您的需求。与 jqPlot 不同,Highcharts 包含连接符,连接符是将标签连接到相应切片的线。

清单 18-40。ch18_06.html

legend: {

enabled: false

},

tooltip: {

enabled: false

},

series: [{

type: 'line',

data: data3,

xAxis:1,

color: ['#ddddaa'],

lineWidth: 4,

dashStyle: 'dash',

marker: {

fillColor: 'rgba(0,0,0,1)',

lineWidth: 4,

lineColor: '#ddddaa',

radius: 7

}

},{

type: 'columnrange',

data: data1,

borderWidth: 0,

xAxis:0

},{

type: 'pie',

name: 'Budget',

data: data2,

borderColor: '#888888',

borderWidth: 1,

center: [370, 40],

size: 100,

showInLegend: false,

dataLabels: {

enabled: true,

color: '#bbbbbb',

connectorColor: '#bbbbbb',

formatter: function() {

return '<b>'+ Math.round(this.percentage) +'%</b>';

}

},

xAxis:1

}]

}

$('#myChart').highcharts(options);

完成后的组合图如图 18-21 所示。

A978-1-4302-6290-9_18_Fig21_HTML.jpg

图 18-21。

A combined chart with three different types of chart

高库存图书馆

当您访问 Highcharts 的官方网站时,除了这个库,您还会发现指向另一个名为 Highstock 的库的指示。该库完全集成到 Highcharts 中,并提供了一系列将在您的图表中进一步引入的功能和工具。它允许您创建非常高级的导航工具,将您的图表转换为真正的工具,用于对覆盖很长时间的大量数据进行专业分析。

如果你对图表开发的兴趣导致你面对非常复杂的专业需求,特别是在经济分析方面,这个库将被证明是一个不可或缺的工具。因为它的内容主要包括集成到现有图表中的工具以及它们各自对特定需求的高度特异性,所以本书不讨论这个主题。不过,我强烈建议您通过官方网站上提供的各种演示来看看它的内容( www.highcharts.com/products/highstock )。在这方面也存在大量的文档(参见 http://api.highcharts.com/highstock 的 API Highstock 参考)。

摘要

本章涵盖了 Highcharts 库,是本书第二部分的结尾。您已经看到这个库如何继承了 jqPlot 的所有基本特性,并将其扩展到更专业的水平。

通过实现一个简单的折线图的基础,本章比较了两个库,突出了相同点和不同点。然后,您看到了如何在 Highcharts 打开的情况下处理组件的定制,以及如何从文件中读取数据。

有了 Highcharts,你就实现了其他类型的图表,比如甘特图、主图和细节图,这两种图表都强调了这个库所提供的增加的开发可能性。

最后,再次以条形图和饼图为例,您了解了 Highcharts 如何允许您在同一绘图区域中同时组合几种不同类型的图表,进一步扩展了开发更具创新性和引人注目的图表的可能性。

下一章以讨论 D3 库开始了本书的第三部分,也是最后一部分。它介绍了该库的所有基本概念,并提供了一些基本的入门示例。您会发现它是 jqPlot、Highcharts 和其他基于 jQuery 和 Canvas 的类似库的替代品。您将看到这个库如何使用 SVG 技术创建构建图表所需的所有图形元素,就像它们是小砖块一样。最后,你将分析到目前为止你所看到的一切的优势和差异。