十、jqPlot 条形图

Abstract

在这一章中,你将处理另一大类图表:条形图。在前一章中,您已经了解了 jqPlot 中默认图表类型——折线图的特征。现在,使用 BarRenderer 插件,您将发现主 jqPlot 对象的结构是如何随着新的属性和对象而逐渐丰富的。通过实际例子,你会看到如何用rendererOptions改变属性和对象属性的值。

在这一章中,你将处理另一大类图表:条形图。在前一章中,您已经了解了 jqPlot 中默认图表类型——折线图的特征。现在,使用 BarRenderer 插件,您将发现主 jqPlot 对象的结构是如何随着新的属性和对象而逐渐丰富的。通过实际例子,你会看到如何用rendererOptions改变属性和对象属性的值。

有时,使用同一组数据可以获得不同的表示。学习如何选择最适合你需求的表现形式是本书的基本目标之一。为此,使用一组数据,您将看到如何从分组条形图切换到堆叠条形图,在这两种情况下都可以在垂直和水平表示之间进行选择。

此外,您将了解如何使用 jqPlot 库来表示组合图表,例如,如何同时表示折线图和条形图,以及如何通过降低绘图速度来获得简单但引人注目的动画。,您还将熟悉一种特殊类型的条形图,Marimekko 图,它是由 jqPlot 库以非常令人满意的方式实现的。

在本章的最后一部分,我将介绍 jqPlot 中事件的使用。这是一个复杂的主题,但是由于特殊的 jQuery 函数,您只需几行代码就可以实现显著的交互效果。本章最后给出了这方面的一个典型例子:如何定制工具提示。

使用 BarRenderer 插件创建条形图

当您有一组分为不同类别的数据,并且需要将这些类别相互比较时,条形图可能是最适合您需求的表示形式。您已经看到,在不包含任何插件的情况下,默认情况下传入的数据被解释为连接起来形成一条线的点。为了告诉 jqPlot 输入的数据必须用于绘制条形图,您必须在 HTML 页面的<head>部分放置一组插件:

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

<script type="text/javascript"

src="../src/plugins/jqplot.canvasAxisTickRenderer.min.js"></script>

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

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

或者,如果您更喜欢使用内容交付网络(CDN)服务,您可以按照以下方式进行:

<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.dateAxisRenderer.min.jsT2】

<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.canvasAxisTickRenderer.min.jsT2】

<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.categoryAxisRenderer.min.jsT2】

<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.barRenderer.min.jsT2】

要设置输入数据以便在条形图中使用,您必须插入一个格式为[label,y]的数组,其中 x 不再出现,但一个指示性标签取而代之。这个标签通常是一个字符串值。事实上,当我们谈论条形图时,我们不再对跟踪一个变量(y 值)相对于另一个变量(x 值)的趋势感兴趣,而是对比较数据(标签)的类别或组感兴趣。对于此示例,您将使用五个组,每个组代表一个州,作为标签报告:

var data = [['Germany', 12], ['Italy', 8], ['Spain', 6], ['France', 10], ['UK', 7]];

一旦包含了 BarRenderer 插件,就必须激活它,将它的引用分配给series对象中的renderer属性(参见清单 10-1)。您将对第二个插件 CategoryAxisRenderer 做同样的事情,只为xaxis对象指定它。

清单 10-1。ch10_01a.html

var options = {

title: 'Foreign Customers',

series:[{renderer:$.jqplot.BarRenderer}],

axes: {

xaxis: {

renderer: $.jqplot.CategoryAxisRenderer

}

}

};

$.jqplot ('myChart', [data], options);

接下来,在 HTML 页面的<body>部分,添加以下行:

<div id="myChart" style="height:300px; width:500px;"></div>

这样,你就得到一个简单的条形图,如图 10-1 所示。数据数组中包含的每个状态都由一个蓝色条表示,其高度对应于 y 值。

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

图 10-1。

A simple bar chart

旋转轴刻度标签

通常,可能需要或希望旋转 x 轴上报告的刻度标签。例如,文本可能太长而无法报告,为了保持标签的可读性,您需要将它们写成以一定角度倾斜。这种旋转是通过包含 CanvasTextRenderer 插件实现的:

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

或者,如果您更喜欢使用 CDN 服务,您可以按如下方式操作:

<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.canvasTextRenderer.min.js></剧本>

这里,简单地包含插件是不够的;您还必须通过将它的引用传递给tickRenderer属性来激活它,如清单 10-2 所示。然后,您需要在tickOptions中指定某些属性:您将angle属性设置为–30 度。使用此值,您可以指示文本相对于 x 轴的倾斜度。如果该值为正值,文本将顺时针方向旋转;如果为负(如清单 10-2 所示),则逆时针旋转。

清单 10-2。ch10_01b.html

var options = {

title: 'Foreign customers',

series:[{ renderer: $.jqplot.BarRenderer }],

axes: {

xaxis: {

renderer: $.jqplot.CategoryAxisRenderer,

tickRenderer: $.jqplot.CanvasAxisTickRenderer,

tickOptions: {

angle: -30,

fontSize: '10pt'

}

}

}

};

$.jqplot ('myChart', [data], options);

如果您现在在浏览器中加载网页,在图表的底部,您会看到所有标签都相对于 x 轴逆时针旋转(见图 10-2 )。

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

图 10-2。

A bar chart with rotated labels on the x axis

修改条形之间的间距

使用条形图时,最常见的需求可能是改变条形之间的间距。这个间距可以通过设置不同值的barMargin属性来直接调整。因为这个属性不属于jqplot对象,而是特定于 BarRenderer 插件,所以您必须在rendererOptions中指定它。每当您包含一个渲染器插件时,您也包含了一组不属于原始jqplot对象的全新属性。所以,如果你想要一个不同于这些属性的默认值,你需要在rendererOptions中写这个属性,设置新的值。例如,让我们在条形之间应用一个 30 像素的空间,如清单 10-3 所示。

清单 10-3。ch10_02.html

var options = {

title: 'Foreign Customers',

seriesDefaults:{

renderer:$.jqplot.BarRenderer,

rendererOptions: {

barMargin: 30

},

},

axes: {

因为图表的宽度保持不变,作为将barMargin属性增加到 30 的结果,所有的条形都比以前窄了(见图 10-3 )。

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

图 10-3。

The space between bars is adjustable with a new property introduced by the plug-in

在条形顶部添加值

jqPlot 库允许您处理偶数点标签。虽然您也可以在折线图中使用它们,但是点标签是条形图的重要组成部分。如果激活点标签,将在条形上方明确显示 y 值,从而增强值的可读性,尤其是对于堆积条形图。要激活此功能,您需要包括另一个插件:

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

或者,如果您更喜欢使用 CDN 服务,您可以按如下方式操作:

<script type="text/javascript"

src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.pointLabels.min.jsT2】

您可能已经注意到 PointLabels 不是渲染器插件,因此它已经处于活动状态。这一次,不需要在renderer属性中传递引用。这个过程非常简单快捷:在options中,你将pointLabels对象的show属性设置为'true'(见清单 10-4)。

清单 10-4。ch10_03.html

seriesDefaults:{

renderer:$.jqplot.BarRenderer,

pointLabels: { show: true }

},

如图 10-4 所示,激活点标签后,y 值将出现在每个条的上方。

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

图 10-4。

A bar chart reporting the y value above each bar

具有负值的条形

一般来说,我们习惯于看到所有 y 值都为正的条形图,但情况并非总是如此。但是,如果您想在条形图上表示负值,就必须小心。如果您尝试使用包含负值的输入数据数组,如下例所示

var data = [['Germany', -12], ['Italy', -8], ['Spain', -6], ['France', -10], ['UK', -7]];

你得到了图 10-5 中的条形图。

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

图 10-5。

This bar chart has interpreted the negative values badly

这不是你真正想要的。这些条被画出来,好像它们仍然是正的。只有点标签正确显示 y 值。此外,y 轴上报告的值与条形的表示不相关,条形应该从顶部开始,向下到 y 轴上相应的负值。为了克服所有这些问题,您需要将fillToZero属性设置为'true',这是一个属于 BarRenderer 插件的属性;因此,你必须在rendererOptions中指定这一点(见清单 10-5)。

清单 10-5。ch10_04a.html

var options = {

title: 'Foreign Customers',

seriesDefaults:{

renderer:$.jqplot.BarRenderer,

rendererOptions: { fillToZero: true },

pointLabels: { show: true }

},

...

现在,jqPlot 可以正确地表示负棒线(见图 10-6 )。

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

图 10-6。

A simple bar chart with negative values

当正值和负值都出现在同一个条形图中时,此功能更容易理解:

var data = [['Germany', -12], ['Italy', 8], ['Spain', -6], ['France', 10], ['UK', -7]];

正如你在图 10-7 中所看到的,稍微深一点的颜色区分了具有负值的条形。

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

图 10-7。

A simple bar chart with positive and negative values

包含多组数据的条形图

你已经看到了折线图是如何管理多个序列的,所以你可能认为条形图也有同样的可能性。在从单系列到多系列的转换中,您需要对输入数据的组织方式进行一些更改。因此,您从单个序列的输入数据数组的格式开始:

var data = [['Germany', 12], ['Italy', 8], ['Spain', 6], ['France', 10], ['UK', 7]];

首先,您必须指定一个定制的ticks数组,它必须包含数据的组或类别的名称(您想要在 x 轴上报告的值)。刻度数应该与每个系列中 y 值的数量相匹配。

var ticks = ['Germany', 'Italy', 'Spain', 'France', 'UK'];

因为您正在处理多个系列,所以您可以指定至少三个数据系列。每个系列代表数据的进一步分类,因此您可以通过报告数据所属组的标签来区分它们。现在,你只需要为每个序列插入 y 值(来自ticks数组),如清单 10-6 所示,假设每个序列的 x 值是相同的。

清单 10-6。ch10_05.html

var data = [12, 8, 6, 10, 7];     // Electronics customers

var data2 = [14, 12, 4, 14, 11];  // Software customers

var data3 = [18, 10, 5, 9, 9];    // Mechanics customers

关于表示系列的名称,你必须在series对象中指定它们(见清单 10-7),将它们逐个分配给每个系列的label属性。

清单 10-7。ch10_05.html

var options = {

title: 'Foreign Customers',

seriesDefaults:{

renderer:$.jqplot.BarRenderer,

},

series:[

{label: 'Electronics'},

{label: 'Software'},

{label: 'Mechanics'}

],

axes: {

...

现在,和往常一样,在options中,你将ticks数组赋给xaxis对象的ticks属性(见清单 10-8)。这样,您已经将 x 轴上生成的每个记号分配给数组中包含的一个字符串。

清单 10-8。ch10_05.html

axes: {

xaxis: {

renderer: $.jqplot.CategoryAxisRenderer,

ticks: ticks

}

},

您已经看到,处理多个系列只是增加了数据的进一步分类。除了划分到 x 轴上表示的类别之外,数据还划分到多个系列中,每个系列表示一个不同的组。为了区分一个系列和另一个系列,需要用不同的颜色绘制相应的线条。但是,如果您就此打住,观察此图表的用户将不会获得任何关于哪个组由哪种颜色表示的信息。因此需要引入一个图例(见清单 10-9)。

清单 10-9。ch10_05.html

var options = {

title: 'Foreign Customers',

seriesDefaults: {

renderer: $.jqplot.BarRenderer,

},

series:[

{label: 'Electronics'},

{label: 'Software'},

{label: 'Mechanics'}

],

axes: {

xaxis: {

renderer: $.jqplot.CategoryAxisRenderer,

ticks: ticks

}

},

legend: {

show: true,

placement: 'outsideGrid',

location: 'e'

}

};

$.jqplot ('myChart', [data, data2, data3], options);

这样你就得到一个多系列条形图,如图 10-8 所示。正如您所看到的,当您使用多个系列时,您必须使用不同的颜色来区分它们。

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

图 10-8。

A multiseries bar chart containing a legend

垂直和水平条形图

查看图 10-8 中的图表,您会注意到每组都用一种颜色表示。默认情况下,分配的颜色遵循 jqPlot 内部指定的顺序,该顺序反映在折线图中。每个国家在 x 轴上的区段中有三列,每个区段由网格线界定。

这种条形图一般定义为垂直条形图。没有什么可以阻止我们用水平方向的条来表示相同的输入数据,但是这里也需要对输入数据数组的格式进行修改。在这种情况下,有必要使用[y,n]对,其中n是分配给一个字符串的整数值(见清单 10-10)。字符串是包含在ticks数组中的标签描述,n是它的索引。

清单 10-10。ch10_06.html

var data =  [[12, 1], [8, 2], [6, 3], [10, 4], [7, 5]];

var data2 =  [[14, 1], [12, 2], [4, 3], [14, 4], [11, 5]];

var data3 =  [[18, 1], [10, 2], [5, 3], [9, 4], [9, 5]];

var ticks = ['Germany', 'Italy', 'Spain', 'France', 'UK'];

更改输入数据数组的格式后,必须将barDirection属性设置为'horizontal' ( 'vertical'是默认值)。如清单 10-11 所示,您必须在seriesDefaults中这样做,以便将水平方向应用于所有系列。这一次,需要将ticks数组赋给yaxis对象中的ticks属性,而不是像以前一样赋给xaxis对象。

清单 10-11。ch10_06.html

var options = {

title: 'Foreign Customers',

seriesDefaults:{

renderer: $.jqplot.BarRenderer,

rendererOptions: {

barDirection: 'horizontal'

}

},

series:[

{label: 'Electronics'},

{label: 'Software'},

{label: 'Mechanics'}

],

axes: {

yaxis: {

renderer: $.jqplot.CategoryAxisRenderer,

ticks: ticks

}

},

legend: {

show: true,

placement: 'outsideGrid',

location: 'e'

}

};

$.jqplot ('myChart', [data, data2, data3], options);

现在,你得到了如图 10-9 所示的水平多系列条形图。

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

图 10-9。

A horizontal multiseries bar chart

垂直堆积条形图

当您需要将数据系列分解成其组成部分,同时保留将这些数据系列作为一个整体进行比较的能力时,您必须使用堆积图。jqPlot 库支持这样的图表。对于堆积条形图,添加点标签尤其合适,这样可以使图表更具可读性。报告的值是累积的,也就是堆栈中底层条形的总和,如清单 10-12 所示。

清单 10-12。ch10_07.html

var data = [12, 8, 6, 10, 7];

var data2 = [14, 12, 4, 14, 11];

var data3 = [18, 10, 5, 9, 9];

var ticks = ['Germany', 'Italy', 'Spain', 'France', 'UK'];

var options = {

title: 'Foreign Customers',

stackSeries: true,

seriesDefaults:{

renderer:$.jqplot.BarRenderer,

pointLabels: { show: true,location: 's' }

},

series:[

{label: 'Electronics'},

{label: 'Software'},

{label: 'Mechanics'}

],

axes: {

xaxis: {

renderer: $.jqplot.CategoryAxisRenderer,

ticks: ticks

}

},

legend: {

show: true,

placement: 'outsideGrid',

location: 'e'

}

};

$.jqplot ('myChart', [data, data2, data3], options);

通过设置pointLabels属性的值,可以指定显示点标签的位置:'n''s'' e''w''ne''nw''se''sw'。这些值应该被解释为指示绘制点标签的方向的基本点,相对于条形的顶部。在本例中,您选择's' (south)在条形顶部下方的彩色区域显示数值,如图 10-10 所示。

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

图 10-10。

A vertical multiseries stacked bar chart

水平堆叠条形图

同样,您可以创建水平堆积条形图。在这种情况下,为了表示线段内的点标签,你需要将它们设置为'w' (west)(见清单 10-13)。

清单 10-13。ch10_08.html

var data =  [[12, 1], [8, 2], [6, 3], [10, 4], [7, 5]];

var data2 =  [[14, 1], [12, 2], [4, 3], [14, 4], [11, 5]];

var data3 =  [[18, 1], [10, 2], [5, 3], [9, 4], [9, 5]];

var ticks = ['Germany', 'Italy', 'Spain', 'France', 'UK'];

var options = {

title: 'Foreign Customers',

stackSeries: true,

seriesDefaults:{

renderer: $.jqplot.BarRenderer,

rendererOptions: {

barDirection: 'horizontal'

},

pointLabels: { show: true, location: 'w' }

},

series:[

{label: 'Electronics'},

{label: 'Software'},

{label: 'Mechanics'}

],

axes: {

yaxis: {

renderer: $.jqplot.CategoryAxisRenderer,

ticks: ticks

}

},

legend: {

show: true,

placement: 'outsideGrid',

location: 'e'

}

};

$.jqplot ('myChart', [data, data2, data3], options);

在图 10-11 中,您可以看到数值是如何显示在靠近条形末端(西部)的彩色区域。

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

图 10-11。

A horizontal multiseries stacked bar chart

组合图表:条形图中的线条

组合图表是在单个图表中组合两种或多种图表类型的图表。在下面的示例中,您将考虑在一个图表中同时显示条形图系列和折线图系列。对于这种表示,您需要使用一个双 y 轴。每个系列都有自己的单位和大小,因此必须符合这些轴之一。因此,你必须使用主轴和副轴。您还必须激活自动缩放功能,以便强制 y 轴对齐刻度线,从而获得一致的网格线。

因此,让我们定义两个输入序列(见清单 10-14)。数组data包含要显示为条形图的[label1,y1]对数值。数组line包含要显示为折线图的[label2,y2]对数值。

清单 10-14。ch10_09.html

var data = [['Germany', 12], ['Italy', 8], ['Spain' ,6], ['France', 10], ['UK', 7]];

var line = [['BMW', 45], ['AlfaRomeo', 30], ['Seat', 24],['Renault', 36], ['Mini', 30]];

这个案例有助于理解使用多个 y 轴的效用(jqPlot 最多支持九个 y 轴和两个 x 轴)。这里,您有两个序列,它们的顺序由您在函数$.jqplot()中作为第二个参数传递它们的顺序来定义:

$.jqplot ('myChart', [data, line], options);

数组data(用于条形图的系列)排在第一位,数组line(用于折线图)排在第二位。这一点非常重要。建立了这个顺序后,在options中,您需要在series对象中指定两个元素,如清单 10-15 所示。仅在第一个元素中,您激活了 BarRenderer 插件,而在第二个元素中,您定义了两个辅助轴:x2axisy2axis。现在,您有四个轴可以使用,因此,您必须在axes对象中指定它们。在yaxisy2axis上,您必须激活自动缩放。

清单 10-15。ch10_09.html

var options = {

title: 'Foreign customers',

series:[{renderer: $.jqplot.BarRenderer},

{

xaxis: 'x2axis',

yaxis: 'y2axis'

}],

axes: {

xaxis: {

renderer: $.jqplot.CategoryAxisRenderer

},

x2axis: {

renderer: $.jqplot.CategoryAxisRenderer

},

yaxis: {

autoscale: true

},

y2axis: {

autoscale: true,

renderOptions: {

alignTicks: true

}

}

}

};

其结果是图 10-12 中的图表,同时包含条和线。

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

图 10-12。

A line chart combined with a bar chart

动画情节

jqPlot 库还为您提供了动画图表的能力。为此,您不需要任何额外的插件。从前面的例子开始,组合图(见清单 10-15),你可以为每个系列分配不同的速度。在定义绘图速度时,就好像您正在减慢浏览器创建图表元素的速度。这会在绘制过程中产生动态效果,从而创建动画。此外,通过分配不同的速度给不同的部分,你可以获得非常好的效果。

正如我们在清单 10-16 中看到的,在options中,您必须通过将animateanimateReplot属性设置为'true'来激活动画功能。然后,使用数值(毫秒数)为每个系列定义不同的速度。

清单 10-16。ch10_10.html

var options = {

animate: true,

animateReplot: true,

title: 'Foreign Customers',

series:[{

renderer: $.jqplot.BarRenderer,

rendererOptions: {

animation: {

speed: 2500

},

}

},{

xaxis: 'x2axis',

yaxis: 'y2axis',

rendererOptions: {

animation: {

speed: 2500

},

}

}],

axes: {

xaxis: { renderer: $.jqplot.CategoryAxisRenderer  },

x2axis: {renderer: $.jqplot.CategoryAxisRenderer  },

yaxis: { autoscale:true, numberTicks: 6 },

y2axis: { autoscale:true, numberTicks: 6 }

}

};

当您在浏览器中加载此图表时,您会获得一个动画,其中缓慢而平滑地绘制了一个折线图和一个条形图。图 10-13 显示了动画如何在连续的阶段中发展。折线图是按照数据点的顺序从左到右绘制的,同时,条形会增长到各自的 y 值。

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

图 10-13。

An animated combined line–bar chart

马里梅科海图

一种可以从条形图派生出来的图表是所谓的 Marimekko 图表(也称为 mekko 图表),因其与 Marimekko 印刷品相似而得名。这种图表已经被商业界所采用。Marimekko 图表本质上是堆叠柱形图。然而,这里所有的栅栏都是一样高的。此外,条形之间没有空间,条形被分成几段,其高度与百分比相关(见图 10-14 )。

Marimekko 图表旨在报告两个轴上的百分比值:x 轴上每个条形所在位置的每个类别所占的百分比,以及 y 轴上每个类别所占的百分比,由每个条形所划分的段表示。

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

图 10-14。

A Marimekko pattern

jqPlot 允许您使用两个特定的插件来开发这种图表:MekkoRenderer 和 MekkoAxisRenderer:

<script class="include" type="text/javascript"

src="../src/plugins/jqplot.mekkoRenderer.min.js"></script>

<script class="include" type="text/javascript"

src="../src/plugins/jqplot.mekkoAxisRenderer.min.js"></script>

<script class="include" type="text/javascript"

src="../src/plugins/jqplot.canvasTextRenderer.min.js"></script>

<script class="include" type="text/javascript"

src="../src/plugins/jqplot.canvasAxisLabelRenderer.min.js"></script>

或者,如果您更喜欢使用 CDN 服务,您可以按如下方式操作:

<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.mekkoRenderer.min.js></剧本>

<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.mekkoAxisRenderer.min.js></剧本>

<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.canvasTextRenderer.min.js></剧本>

<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.canvasAxisLabelRenderer.min.js></剧本>

除了这两个插件,您还需要包括 CanvasTextRenderer 和 CanvasAxisLabelRenderer。为图表中的每个条形指定数据。您可以将数据指定为 y 值数组或[标签,值]对数组。在清单 10-17 中,注意标签只用于第一个系列;后续系列的标签将被忽略。

清单 10-17。ch10_11.html

var bar1 = [['bananas', 10],['apples', 7],['pears', 4],

['peaches', 8],['lemons', 7],['oranges',5]];

var bar2 = [9, 5, 8, 11, 9, 4];

var bar3 = [11, 4, 7, 3, 8, 7];

var bar4 = [5, 8, 11, 4, 12, 3];

var barLabels = ['Italy', 'Spain', 'France', 'Greece'];

options中,您激活 MekkoRenderer 插件,将其分配给seriesDefaults对象。通过将legend对象的show属性设置为'true',可以在图表的右侧添加一个图例。如果您想在 x 轴下方放置每个条形的标签,您必须将barLabels数组分配给 x 轴上的barLabels属性。

清单 10-18。ch10 _ 11 . html[无标注]

var options = {

title: 'Fruit Consumption in 2012',

seriesDefaults:{renderer: $.jqplot.MekkoRenderer},

legend:{show: true},

axesDefaults:{

renderer: $.jqplot.MekkoAxisRenderer

},

axes:{

xaxis:{

barLabels: barLabels,

tickOptions:{formatString: '%d'}

}

}

};

$.jqplot('myChart', [bar1, bar2, bar3, bar4], options);

现在,你得到了如图 10-15 所示的 Mekko 图。

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

图 10-15。

A Mekko chart

条形图事件

在条形图中,如果您将光标移动到一个条上,默认情况下它会高亮显示。当鼠标悬停在某个条上时,以及当您单击某个条时,都会触发事件。捕捉和管理这些事件的能力非常重要,jqPlot 库允许您这样做。您可以为不同类型的事件实现特定的响应操作,从而使您的图表更具交互性。您获得的响应可能取决于您找到鼠标指针的位置或您单击的目标。

10-1 报告的事件,由于其丰富的元素,适合在条形图中应用。让我们一个一个地看看这些事件。

表 10-1。

Handling Events with the jqPlot Library

| 事件 | 被触发时 | | --- | --- | | jqplotDataClick | 在数据点上单击鼠标左键。 | | jqplotRightClick | 用鼠标右键单击数据点。 | | jqplotDataMouseOver | 你把鼠标放在数据点上。 | | jqplotDataHighlight | 数据点被突出显示。 | | jqplotDataUnhighlight | 数据点未突出显示。 |

jqplotDataClick 事件

本示例显示了jqplotDataClick事件——被点击的序列索引、点及其数据值。

让我们从第一个例子开始,简单的条形图(见清单 10-19)。

清单 10-19。ch10_12.html

var data = [['Germany', 12], ['Italy', 8], ['Spain', 6],

['France', 10], ['UK', 7]];

var options = {

title: 'Foreign Customers',

series:[{renderer: $.jqplot.BarRenderer}],

axes: {

xaxis: {

renderer: $.jqplot.CategoryAxisRenderer

}

}

};

$.jqplot ('myChart', [data], options);

在 jQuery ready()函数中,添加清单 10-20 中的函数。这是一个 jQuery 函数,其中将jqplotDataClick事件与函数的执行绑定在一起。作为参数,该事件接受 jqPlot 对象的一些属性值,如seriesIndexpointIndexdata。这些值将被转换成一个字符串,并用 jQuery html()函数连接起来。这个 HTML 文本将被发送到网页中的info1元素。

清单 10-20。ch10_12.html

$('#myChart').bind('jqplotDataClick',

function (ev, seriesIndex, pointIndex, data) {

$('#info1').html('series: ' + seriesIndex +

', point: '+pointIndex+', data: '+data);

}

);

现在,您在想要显示带有值的文本的地方添加一个<span>元素。这个元素将显示一个“还没有”的消息,直到你点击一个酒吧。然后,根据所点击的点,新文本将用值替换消息:

<div><span>You clicked: </span><span id="info1">Nothing yet</span></div>

10-16 显示了当用户点击“France”栏时触发的事件对应的消息。

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

图 10-16。

By clicking a bar, you can obtain its values

jqplotRightClick 事件

这个例子涵盖了 jqPlot 提供的另一个事件:jqplotRightClick。这个事件需要在options中显式激活(见清单 10-21)。这导致 jqPlot 在用户右键单击一个栏时触发一个jqplotRightClick事件。

清单 10-21。ch10_13.html

var options = {

title: 'Foreign Customers',

captureRightClick: true,

series:[{renderer: $.jqplot.BarRenderer}],

axes: {

xaxis: {

renderer: $.jqplot.CategoryAxisRenderer

}

}

};

接下来,您需要用清单 10-22 中的函数替换前面的 jqPlot 函数。

清单 10-22。ch10_13.html

$('#myChart').bind('jqplotDataRightClick',

function (ev, seriesIndex, pointIndex, data) {

$('#info1').html('series: ' + seriesIndex +

', point: '+pointIndex+', data: '+data);

}

);

一般效果是一样的,只是这次你要右击而不是左键。右键单击“西班牙”栏会得到图 10-17 中的结果。

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

图 10-17。

By right-clicking a bar, you obtain its values

其他条形图事件

通常,您可能想要捕获另一个事件:当鼠标停留在一个条上时,jqPlot 触发一个jqplotDataMouseOver事件。当您显式禁用突出显示时,也会生成此事件。当用户将鼠标放在栏上时,该事件将持续触发。相比之下,另一个事件jqplotDataHighlight只触发一次,即当用户第一次将鼠标放在工具栏上时。当用户离开酒吧时,jqPlot 触发第三个事件:jqplotDataUnhighlight。只有启用突出显示时,才会生成后两个事件。

继续上一个例子(见清单 10-22),你用另外两个函数替换捕获jqplotDataClick事件的 jQuery 函数(见清单 10-23)。第一个函数会在您将鼠标放在一个栏上时,立即向info1元素发送一个具有相同值的 HTML 文本。第二个将在您离开工具栏时用'Nothing'替换info1元素中的前一个字符串。

清单 10-23。ch10_14a.html

$('#myChart').bind('jqplotDataHighlight',

function (ev, seriesIndex, pointIndex, data) {

$('#info1').html('series: ' + seriesIndex +

', point: '+pointIndex+', data: '+data);

}

);

$('#myChart').bind('jqplotDataUnhighlight',

function (ev) {

$('#info1').html('Nothing');

}

);

现在,您希望看到jqplotDataMouseOver事件和jqPlotDataHighlight事件之间的行为差异。要理解这种差异,最好的方法就是用一个例子来比较它们。这一次,您将计算当鼠标悬停在一个条上时触发的事件数。为此,定义一个计数器nEvents,将其初始化为 0。正如预期的那样,使用jqPlotDataHighlight事件,每当您将鼠标放在一个条上时,计数器被设置为 1,当您移出该条时,计数器的值为 0。对于jqplotDataMouseOver事件,行为非常不同:计数器持续增加,将光标保持在同一个条上。在这两种情况下,您都使用jqplotDataUnhighlight事件在每次离开酒吧时重置计数器。

首先,您需要更改info1 HTML 元素:

<div><span>Events: </span><span id="info1">Nothing yet</span></div>

然后,为了研究jqplotDataHighlight事件的行为,用清单 10-24 中的两个函数替换这两个 jQuery 函数。

清单 10-24。ch10_14b.html

nEvents = 0;

$('#myChart').bind('jqplotDataHighlight',

function (ev, seriesIndex, pointIndex, data) {

nEvents = nEvents + 1;

$('#info1').html(nEvents);

}

);

$('#myChart').bind('jqplotDataUnhighlight',

function (ev) {

$('#info1').html('Nothing');

nEvents = 0;

}

);

如图 10-18 所示,当鼠标停留在“西班牙”栏上时,计数器给出稳定的 1。

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

图 10-18。

Counting how many jqPlotDataHighlight events occur

为了研究jqplotDataMouseOver事件的行为,您可以用清单 10-25 中的两个函数替换这两个 jQuery 函数。

清单 10-25。ch10_14c.html

nEvents = 0;

$('#myChart').bind('jqplotDataMouseOver',

function (ev, seriesIndex, pointIndex, data) {

nEvents = nEvents + 1;

$('#info1').html(nEvents);

}

);

$('#myChart').bind('jqplotDataUnhighlight',

function (ev) {

$('#info1').html('Nothing');

nEvents = 0;

}

);

如图 10-19 所示,当鼠标停留在“西班牙”栏上时,计数器不断增加数值。

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

图 10-19。

Counting how many jqplotDataMouseOver events occur

点按栏以文本形式显示信息

由于 jqPlot 在管理事件方面的潜力,让我们借此机会看一个常见的案例。通过单击一个条形,您可以获得有关该条形的信息,并将其显示在 HTML 页面上的文本框中。这是通过将侦听器绑定到jqlotDataClick事件来实现的。

在这个例子中,我们从生成水平堆积条形图的代码开始(见清单 10-26)。

清单 10-26。ch10_15.html

var data = [12, 8, 6, 10, 7];

var data2 = [14, 12, 4, 14, 11];

var data3 = [18, 10, 5, 9, 9];

var ticks = ['Germany', 'Italy', 'Spain', 'France', 'UK'];

var options = {

title: 'Foreign Customers',

stackSeries: true,

seriesDefaults:{

renderer: $.jqplot.BarRenderer,

pointLabels: { show: true, location: 's' }

},

series:[

{label: 'Electronics'},

{label: 'Software'},

{label: 'Mechanics'}

],

axes: {

xaxis: {

renderer: $.jqplot.CategoryAxisRenderer,

ticks: ticks

}

},

legend: {

show: true,

placement: 'outsideGrid',

location: 'e'

}

};

$.jqplot ('myChart', [data, data2, data3], options);

和前面的事件示例一样,最后您添加一个 jQuery 函数来捕获jqplotDataClick事件,并向info1元素发送一组信息(参见清单 10-27)。

清单 10-27。ch10_15.html

$('#myChart').bind('jqplotDataClick',

function (ev, seriesIndex, pointIndex, data) {

$('#info1').html('series: ' + seriesIndex +

', point: '+pointIndex+', data: '+data);

}

);

每当您点击一个条时,该功能将刷新您放置了<span>元素的地方显示的信息,其中'info1'id。在这里,您将<span>元素添加到 HTML 页面:

<span id="info1">Information will be provided here </span>

现在,通过单击一个条形的突出显示区域,您可以在文本框中获得与该条形相关的所有数据,如图 10-20 所示。

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

图 10-20。

By clicking a stacked bar, you obtain its values

处理图例

使用条形图时,您利用了图例,这是大多数图表的关键组成部分。图例是 jqPlot 中定义的元素。通常,您只需要调用options中的legend对象,就可以在图表旁边弹出图例。在这里,您将更详细地分析这个有用的元素。

什么时候有必要使用图例?当您处理多序列数据时,也就是说,当您有一组数据时,通常用不同的颜色来区分。图例除了在一个小空间中报告每种颜色和一个区分该组元素的标签之间存在的关系之外,什么也不做。

添加图例

前面的例子(见图 10-20 )非常适合研究传说。通过观察堆积图,您可以很容易地看到每个国家的条形图由三部分组成,这三部分用不同的颜色表示。因此,您知道图表中显示了三个系列。此外,您还知道每个系列占总价值的比例,但是仍然缺少关键信息:哪些类别由哪些颜色表示。通过添加图例,您将阐明三种颜色与这些类别之间的关联:“机械”、“软件”和“电子”

因此,继续使用前一个例子中的代码(参见清单 10-26 和 10-27),让我们将legend定义添加到options对象中,如清单 10-28 所示。为此,您不必包含任何插件;你只需要将show属性设置为'true'

清单 10-28。ch10_15.html

var options = {

...

axes: {

xaxis: {

renderer: $.jqplot.CategoryAxisRenderer,

ticks: ticks

}

},

legend: {

show: true,

placement: 'outsideGrid',

location: 'e'

}

};

$.jqplot ('myChart', [data,data2,data3],options);

10-21 显示了带有图例的图表。

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

图 10-21。

A stacked bar chart with a legend

placement属性指定您想要图例的位置;省略它,您将获得默认行为:图例绘制在图表内部。为了避免覆盖条形,您可以通过设置location属性来改变图例的位置,如清单 10-29 所示。

清单 10-29。ch10_16a.html

legend: {

show: true,

}

这样,如图 10-22 所示,图表被改变,图例被画在里面(默认),在右上角('ne'【东北】)。

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

图 10-22。

The default legend position is inside the chart, in the top-right corner

对于更彻底的方法,最好使用级联样式表(CSS)定制。您将为图例使用一些 CSS 类来修改默认属性——主要是 CSS 类table.jqplot-table-legend

例如,您可以将规范添加到清单 10-30 提供的 CSS 类中。

清单 10-30。ch10_16b.html

<style>

table.jqplot-table-legend {

background-color: rgba(175, 175, 175, 1);

font: "Arial Narrow";

font-style: italic;

font-size: 13pt;

color: white;

}

</style>

并且,图例将会改变,如图 10-23 所示。

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

图 10-23。

The modified legend, using CSS styles

增强的图例

如果查看 jqPlot 发行版中的几个插件,会发现一个与 legends 相关的插件:EnhancedLegendRenderer。这个插件扩展了图例的功能:单击图例项,可以显示或隐藏相应的序列。用一个具体的例子就可以看出这一点。首先,将插件包含在您的 web 页面中:

<script type="text/javascript"

src="../src/plugins/jqplot.enhancedLegendRenderer.min.js"></script>

或者,如果您更喜欢使用 CDN 服务,您可以按如下方式操作:

<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.enhancedLegendRenderer.min.js></剧本>

然后,你必须激活options中的插件,就像你激活其他插件一样,如清单 10-31 所示。

清单 10-31。ch10_16c.html

legend: {

renderer: $.jqplot.EnhancedLegendRenderer,

show: true,

placement: 'outsideGrid',

location: 'ne'

}

在浏览器中加载页面后,您会得到图 10-24 中的图表。如果单击图例中的某个项目,相应的系列将从图表中消失,留下一个空白。如果您只想分析系列的子集,而忽略其他部分,这将非常有用。因此,让我们单击图例中的“软件”,看看会发生什么。

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

图 10-24。

You can hide a series by selecting an item in the legend

在图 10-24 的图例中,属于软件系列的橙色部分已经消失,项目“软件”被划掉。

这种效果是累积的,你可以一个接一个地隐藏所有的系列。如果您再次单击删除线项目,相应的系列将再次出现在图表中。

自定义图例突出显示

您已经看到了如何使用 jqPlot 默认提供的图例。但是,您可以通过在 HTML 中实现一个简单的表来创建自定义图例,然后动态填充它,用您的系列的标签填充它。因为您必须从头开始创建自己的图例,所以首先需要选择一种样式。你可以通过使用一个外部 CSS 文件或者直接在网页中编写样式来定义 CSS 样式,如清单 10-32 所示。

清单 10-32。ch10_17.html

<style type="text/css">

table.sample {

border-width: thin;

border-spacing: 0px;

border-style: outset;

border-color: rgb(221, 221, 221);

border-collapse: collapse;

}

table.sample th {

border-width: 1px;

padding: 1px;

border-style: inset;

border-color: gray;

}

table.sample td {

border-width: 1px;

padding: 1px;

border-style: inset;

border-color: gray;

}

</style>

您已经使用了三个不同的 CSS 类。第一个指定整个表格的样式。另外两个被定义为分别为标题和单元格指定特定的样式。

在定义了样式之后,为了我们的目的,你可以使用一个简单的条形图的代码(见清单 10-33)。

清单 10-33。ch10_17.html

var data = [['Germany', 12], ['Italy', 8], ['Spain', 6], ['France', 10], ['UK', 7]];

var options = {

title: 'Foreign Customers',

series:[{renderer:$.jqplot.BarRenderer}],

axes: {

xaxis: {

renderer: $.jqplot.CategoryAxisRenderer

}

}

};

$.jqplot ('myChart', [data], options);

下一步是将自定义图例绑定到jqplotDataHighlightjqplotDataUnhighlight事件(见清单 10-34)。您已经看到了这些,以及如何使用 jQuery 方法将对象绑定到它们。在这种情况下,你会做得更多;您将确保整个定制图例是仅用几行 jQuery 动态创建的。这些将包括数据数组。与 jqPlot 提供的默认图例相比,在这里您可以获得比表示组系列成员的标签更多的内容。还可以添加汇总值(使用 JavaScript 函数)或简单的 y 值。

清单 10-34。ch10_17.html

$(document).ready(function(){

var data = ...

var options = ...

$.jqplot ('myChart', [data], options);

$.each(data, function(index, val) {

$('#legend1').append('<tr><td>'+val[0]+'</td><td>'+val[1]+'</td></tr>');

});

$('#myChart').bind('jqplotDataHighlight',

function (ev, seriesIndex, pointIndex, data) {

var color = 'rgb(100%, 90%, 50%)';

$('#legend1 tr').css('background-color', '#ffffff');

$('#legend1 tr').eq(pointIndex+1).css('background-color', color);

});

$('#myChart').bind('jqplotDataUnhighlight',

function (ev, seriesIndex, pointIndex, data) {

$('#legend1 tr').css('background-color', '#ffffff');

});

});

第一个 jQuery 函数处理数据数组的值。另外两个在鼠标经过图例项时将图例绑定到突出显示事件。此外,这些函数还将样式属性的变化绑定到这些事件,在本例中是项目的背景颜色。

现在,您需要定义两个不同的区域来插入图表和图例。您可以使用 HTML 表格来实现这一点。因此,你将清单 10-35 中的代码添加到 HTML 页面的<body>部分。

清单 10-35。ch10_17.html

<table style="margin-left:auto; margin-right:auto;">

<tr>

<td><div id="myChart" style="width:460px; height:340px;"></div></td>

<td><div style="height:340px;">

<table id="legend1" class="sample" >

<tr><th>Nation</th><th>Customers</th></tr>

</table>

</div>

</td>

</tr>

</table>

最后,您可以加载带有新自定义图例的新页面(参见图 10-25 )。

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

图 10-25。

A custom HTML legend

自定义工具提示

除了图例,条形图中另一个非常常用的项目是工具提示。正如有可能使用代码创建自定义图例一样,也有可能自定义工具提示,创建非常新颖的效果。当你将鼠标放在一个条上并高亮显示它时,会显示一个工具提示,但是,与默认的 jqPlot 工具提示不同,它完全是以 HTML 格式构建的。这极大地拓展了你艺术表达的可能性,给你的星盘增添了一丝个性(使用 jqPlot,一切都有过于“标准 jqPlot”的风险)。在这个例子中,你想要展示一个小图标图像如何给一个条形图一个非常好的效果。

在开始编写代码之前,让我们创建一个目录,并将其命名为flags(您可以随意命名)。在这个目录中,您将存储所有要使用的可移植网络图形(PNG)图像文件。这些图标是各国的国旗,您将在条形图的 x 轴上报告。从互联网上很容易找到并下载这些 PNG 文件。

Note

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

完成标志图像后,第一步是创建自定义工具提示。您需要将工具提示绑定到jqplotDataHighlightjqplotDataUnhighlight事件。您可以用几行 jQuery 动态创建定制的工具提示。

这里,你从你已经使用过的条形图开始(见清单 10-36),因为它代表了一个简单的例子来理解开发这种定制工具提示的方法。

清单 10-36。ch10_18.html

var data = [['Germany', 12], ['Italy', 8], ['Spain', 6], ['France', 10], ['UK', 7]];

var options = {

title: 'Foreign Customers',

series:[{renderer:$.jqplot.BarRenderer}],

axes: {

xaxis: {

renderer: $.jqplot.CategoryAxisRenderer

}

}

};

$.jqplot ('myChart', [data], options);

你必须添加另外两个数据数组,包含一些字符串(见清单 10-37)。您将在动态生成的工具提示中使用这些。

清单 10-37。ch10_18.html

var tick = ['Germany', 'Italy', 'Spain', 'France', 'UK'];

var icon = ['germany.png', 'italy.png', 'spain.png', 'france.png', 'uk.png'];

您将jqplot()函数的返回值赋给一个变量,因为您稍后将需要使用它。

var myPlot = $.jqplot ('myChart', [data], options);

清单 10-38 给出了将事件绑定到自定义工具提示的 jQuery 代码。

清单 10-38。ch10_18.html

$('#myChart').bind('jqplotDataHighlight',

function (ev, seriesIndex, pointIndex, data) {

var chart_left = $('#myChart').offset().left;

var chart_top = $('#myChart').offset().top;

var x = data[0]*95+20;

var y = myPlot.axes.yaxis.u2p(data[1]);

var color = 'rgb(30%,50%,60%)';

$('#tooltip1').css({left:chart_left+x, top:chart_top+y});

$('#tooltip1').html('<span style="font-size:16px; font-weight:bold; color:' +

color + ';">' + tick[data[0] - 1] +

'</span><br/><img src="flags/'+ icon[data[0] - 1]+

'" width="30" height="20"><br/> n:' + data[1]);

$('#tooltip1').show();

}

);

$('#myChart').bind('jqplotDataUnhighlight',

function (ev, seriesIndex, pointIndex, data) {

$('#tooltip1').empty();

$('#tooltip1').hide();

}

);

最后,你必须在网页的<body>部分创建两个<div>元素,如清单 10-39 所示。在第一个元素中,jqPlot 将在画布上生成您的自定义工具提示;在第二个例子中,jqPlot 将创建绘制条形图的画布。

清单 10-39。ch10_18.html

<div id="myChart" style="height:300px; width:500px;"></div>

<div id="tooltip1" style="position:absolute; height:0px; width:0px;"></div>

10-26 显示了高亮显示的“德国”栏,旁边有定制的工具提示。这种情况会发生在条形图中的所有条形上,每次用户将鼠标悬停在条形上突出显示它们时,每个条形都会在工具提示中显示相应的标志。

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

图 10-26。

A bar chart with custom tool tips

摘要

在本章中,您已经看到了如何使用 BarRenderer 插件在条形图中表示您的数据。您开始看到,随着这个渲染器插件的引入,jqPlot 主对象的结构逐渐被新的属性和对象所丰富。通过实际的例子,您学习了如何用rendererOptions改变属性和对象属性的值。

您还了解了使用同一组数据有时可能会获得不同的表示。知道如何选择哪种表示更适合你的需求是本书的基本目标之一。为此,您在分组条形图和堆积条形图中使用了相同的数据集。在这两种情况下,您都用垂直和水平条实现了数据表示。

在本章的后面,你看了一些例子,展示了 jqPlot 如何允许你使用特殊函数来处理事件。此外,您还进一步研究了图例组件以及使用 HTML 代码定制图例的可能性。然后,您将相同的方法应用于工具提示。

通常,需要条形图表示的数据类型也可以通过另一种类型的图表来很好地表示:饼图。在下一章中,您将发现 jqPlot 库如何处理这种类型的图表。