十五、将控件添加到图表

Abstract

有时,在运行时直接从浏览器更改设置,然后用这些新设置重新绘制图表会很有用。一种典型的方法是添加活动控件。这些控件使图表具有交互性,允许用户实时做出选择,例如决定图表应该如何表示。通过插入控件,用户可以控制图表的属性值,这通常需要在options中设置。

有时,在运行时直接从浏览器更改设置,然后用这些新设置重新绘制图表会很有用。一种典型的方法是添加活动控件。这些控件使图表具有交互性,允许用户实时做出选择,例如决定图表应该如何表示。通过插入控件,用户可以控制图表的属性值,这通常需要在options中设置。

在这一章中,你将会看到在你的网页中引入控件。您还将考虑导致选择一种控制类型而不是另一种控制类型的因素。以三个最常用的控件为特色的一系列示例将带您更深入地了解这个主题。

添加控件

对控件进行分组的一种方法是根据它们的功能。一些控件(例如,按钮、菜单)作为开关(命令控件)工作,用户可以用其触发特定事件或启动命令。其他控件(例如,复选框、单选按钮、组合框、滑块)绑定到特定的值或属性。使用这种类型的控件,用户可以通过文本字段(文本区域)进行选择或输入值。还有一些控件(例如滚动条)具有导航功能,尤其适用于需要移动对象的情况,例如列表中的选定项或包含在框架或网页中的大图像。

在这里,您将研究那些与值相关联的控件,这些控件允许用户通过选择与图表进行交互。这些控件应该以某种方式图形化地表示特定属性可以采用的值(通常分配给options对象中的属性的相同值,仅限于您希望提供给用户的值)。您对控件的选择将取决于要设置的属性及其可能采用的值:

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

图 15-1。

Three of the most commonly used controls: (a) radio buttons, (b) check boxes, (c) sliders

  • 为了使用户能够从一组值(例如,三种可能的颜色之一)中进行单一选择,选择互斥的单选按钮作为控件是最佳的(参见图 15-1a )。
  • 为了让用户选择哪个系列应该在图表中可见,你需要使用复选框(见图 15-1b )。
  • 为了允许用户在特定属性的值范围内进行选择(例如,通过调整定义颜色的红绿蓝(RGB)值来改变对象的颜色),滑块通常是最佳选择(参见图 15-1c )(在这种情况下,您将使用三个滑块作为控件,分别对应于红色、绿色和蓝色)。

可能的控制列表并没有到此为止。但是,对这些控件背后的机制的理解使图表开发人员能够处理绝大多数情况,包括最复杂的情况。

在下面的示例中,您将发现如何将这三个控件应用于您的图表。

使用单选按钮

为了说明控件的用法,让我们先来看看单选按钮。单选按钮是以列表形式分组的一组小按钮(参见图 15-1a )。它们通常被表示为小而空的圆圈,旁边有文字。如前所述,这种类型的控制与某个值或属性相关联。单选按钮的特殊性在于它们的值是互斥的;因此,用户只能选择其中之一。

举例来说,让我们以一个简单的多系列折线图为例,在该图中,不是显示所有的系列,而是让用户决定显示哪个系列。要进行选择,用户将单击其中一个单选按钮,用圆点填充圆圈。对应于该控件的系列将被绘制在图表上。

添加单选按钮控件

首先,你需要编写 HTML 页面,导入所有必要的库(见清单 11-1)。

清单 15-1。ch15_01.html

<HTML>

<HEAD>

<TITLE>Selection series with controls</TITLE>

<!--[if lt IE 9]>

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

<![endif]-->

<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>

$(document).ready(function(){

//add your code here

});

</script>

</HEAD>

<BODY>

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

<!-- add the table with the controls here -->

</BODY>

</HTML>

或者,如果您更喜欢使用内容交付网络(CDN)服务,您可以使用以下代码:

<!--[if lt IE 9]>

<script src="http://cdn.jsdelivr.net/excanvas/r3/excanvas.jsT2】

<![endif]-->

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

<script type="text/javascript"

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

<link rel="stylesheet" type="text/css"

href="http://cdn.jsdelivr.net/jqplot/1.0.8/jquery.jqplot.min.cssT2】

您从一个折线图开始,在折线图中,您将表示四组值。每个序列中的每个元素将由一对(x,y)值表示;您将这四个序列的值插入到 jQuery $(document).ready()函数中定义的数据集中,如清单 15-2 所示。

清单 15-2。ch15_01.html

var dataSet = {

data1: [[1, 1], [2, 2], [3, 3], [4, 2], [5, 3], [6, 4]],

data2: [[1, 3], [2, 4], [3, 5], [4, 6], [5, 5], [6, 7]],

data3: [[1, 5], [2, 6], [3, 8], [4, 9], [5, 7], [6, 9]],

data4: [[1, 7], [2, 8], [3, 9], [4, 11], [5, 10], [6, 11]]

};

但是,不是像前面看到的那样用不同颜色的线条显示所有四个系列,而是让用户一次只显示一个系列。一旦图表加载到浏览器中,用户将能够选择四个系列中的任何一个并在它们之间切换,而不必加载新的页面。

首先,只表示第一个序列(data1)(见清单 15-3)。

清单 15-3。ch15_01.html

var options = {

seriesDefaults: {

showMarker: false

},

title: 'Series selection',

axes: {

xaxis: {},

yaxis: {

min: 0,

max: 12

}

}

};

var plot1 = $.jqplot('myChart', [dataSet.data1], options);

Note

在这个例子中,您将由$.jqplot()函数返回的值存储在plot1变量中。这允许您访问jqplot对象的内容,更改值,并调用它的方法,包括replot()函数,它允许您再次绘制图表,包括新的更改。

用户将从一组可能的选项中选择一个选项;单选按钮是实现这一目的的最佳选择。因此,让我们为每个单选按钮分配一个系列。正如你在清单 15-4 中看到的,所有的控件(按钮)都包含在一个表格的内部列表中。每个按钮由一个<input>元素指定,其中四个系列也被指定为值。

清单 15-4。ch15_01.html

<table>

<tr>

<td>

<div>

<ul>

<li><input name="dataSeries" value="data1" type="radio" checked />First Series</li>

<li><input name="dataSeries" value="data2" type="radio" />Second Series</li>

<li><input name="dataSeries" value="data3" type="radio" />Third Series</li>

<li><input name="dataSeries" value="data4" type="radio" />Fourth Series</li>

</ul>

</div> </td>

</tr>

</table>

然而,在 HTML 页面中设置控件定义是不够的;您还必须创建将单选按钮与 jqPlot 图表相关联的函数。根据哪个单选按钮处于checked状态,图表中将加载不同于数据集的集合。

在选择不同的单选按钮时,用户将选中的属性从'false'更改为'true'。单选按钮的状态改变涉及到change()功能的激活,该功能检测到该事件。该函数将数据集中的一个新集合分配给plot1变量(包含关于 jqPlot 图表的所有信息),并最终强制重新绘制图表。这样,新数据就显示在图表中,而不必重新加载页面(见清单 15-5)。

清单 15-5。ch15_01.html

$(document).ready(function(){

...

var plot1 = $.jqplot ('myChart', [dataSet.data1], options);

$("input[type=radio][name=dataSeries]").attr("checked", false);

$("input[type=radio][name=dataSeries][value=data1]").attr("checked", true);

$("input[type=radio][name=dataSeries]").change(function(){

var val = $(this).val();

plot1.series[0].data = dataSet[val];

plot1.replot();

});

});

要定制控件表中的元素,可以添加一点层叠样式表(CSS)样式,如清单 15-6 所示。

清单 15-6。ch15_01.html

<style>

li {

font-family: "Verdana";

font-size: 16px;

font-weight: bold;

text-shadow: 1px 2px 2px #555555;

margin: 3px;

list-style: none;

}

</style>

如果您在浏览器中加载此网页,您将获得图 15-2 中的图表。

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

图 15-2。

With radio buttons it is possible to select only one series of data

现在,用户可以选择在图表中显示哪个系列。选择单选按钮作为控件后,图表一次将只显示一组数据。

绘制图表后访问属性

到目前为止,您已经使用了options对象来定义图表的属性值(通过更改默认值),然后将其作为参数传递给$.jqplot()函数。但是,这仅适用于您希望在绘制图表之前描述其特征的情况。如果随后需要访问属性值,该怎么办?

事实上,通过引入控件作为参数,您也引入了在绘制图表后更改这些属性的可能性。因此,必须有一种方法来访问这些值,编辑它们,然后运行命令来重绘图表(就像使用replot()函数时一样)(见清单 15-5)。

您已经看到,您可以接收整个jqplot对象作为由$.jqplot()函数返回的值,并将它存储在一个变量中(在前面的例子中,是plot1变量),以便您可以在以后访问它的内容。

一个jqplot对象实际上包含了定义整个 jqPlot 库的所有对象——它们的属性和方法,并且每个特定的实例(例如plot1)都在特定图表的表示中实现。

因此,当您编写 JavaScript 代码来定义处理特定事件(如用户使用控件)的函数时,您可以访问这些值,并在页面设计完图表后更改它们,然后运行命令以所需的更改重新绘制图表。这增加了图表中所需的交互性。

继续前面的例子(参见清单 15-1 到 15-6),您会注意到这些线条都是用蓝色绘制的。现在让我们做一些更改,这样这次用户可以选择绘制系列的颜色。

要做到这一点,你要在表格中添加另一组控件:第二列单选按钮,每一列代表一种颜色(见清单 15-7)。

清单 15-7。ch15_02.html

<table>

<tr>

<td>

<div>

<ul>

<li><input name="dataSeries" value="data1" type="radio" checked />First series</li>

<li><input name="dataSeries" value="data2" type="radio" />Second series</li>

<li><input name="dataSeries" value="data3" type="radio" />Third series</li>

<li><input name="dataSeries" value="data4" type="radio" />Fourth series</li>

</ul>

</div>

</td>

<td>

<div>

<ul>

<li><input name="colors" value="#4bb2c5" type="radio" checked />Blue</li>

<li><input name="colors" value="#ff3333" type="radio" />Red</li>

<li><input name="colors" value="#44bb44" type="radio" />Green</li>

<li><input name="colors" value="#ffaa22" type="radio" />Orange</li>

</ul>

</div>

</td>

</tr>

</table>

接下来,将清单 15-8 中用粗体突出显示的行添加到 JavaScript 代码中。

清单 15-8。ch15_02.html

$("input[type=radio][name=dataSeries]").attr("checked", false);

$("input[type=radio][name=dataSeries][value=data1]").attr("checked", true);

$("input[type=radio][name=dataSeries]").change(function(){

var val = $(this).val();

plot1.series[0].data = dataSet[val];

plot1.series[0].renderer.shapeRenderer.strokeStyle = col;

plot1.replot();

});

var col = "#4bb2c5";

$("input[type=radio][name=colors]").change(function(){

col = $(this).val();

});

15-3 展示了用户如何在四种不同的颜色中进行选择来决定要表现的系列。这是一个添加控件如何增加用户和图表之间的交互性的例子。

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

图 15-3。

The user can select a different combination of colors and series

使用滑块

在前面的示例中,用户首先通过选中第二列中的一个单选按钮来设置颜色,然后从第一列中选择要用该颜色表示的系列。因此,这一过程包括在两个不同的时间做出的两个选择。这一次,您将保持第一列不变,用户从中选择要显示的系列(互斥),但是在单选按钮列的位置,您将插入一组三个滑块。在这种情况下,用户选择要显示的系列,一旦以预定义的颜色绘制在图表上,他或她就可以通过调整组成它的三个 RGB 值来修改该颜色。现在,你有一个选择,然后进行微调。

当需要通过滚动给定范围内的连续值来更改属性值时,滑块就是所需的控件。在这种情况下,需要三个滑块,每种颜色(红、绿、蓝)一个,以便用户可以调整 RGB 值来获得所需的颜色。

使用前面的例子(参见清单 15-7 和 15-8),首先选择 jQuery 接口库(jQuery UI)来获得滑块(关于如何使用 jQuery UI 小部件实现滑块的详细信息,参见第 2 章)。因此,在将滑块添加到网页之前,必须导入属于此库的所有必要文件:

<link rel="stylesheet"

href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.cssT2】

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

Note

如果您在随本书提供的源代码可用的工作空间中工作(参见附录 A),您可以通过使用以下参考来访问工作空间中已经包含的库:

<link rel="stylesheet" href="../src/css/smoothness/jquery-ui-1.10.3.custom.min.css" />

<script type="text/javascript" src="../src/js/jquery-ui-1.10.3.custom.min.js"></script>

一旦导入了所有文件,就可以开始在 HTML 表格中插入三个滑块了。正如您在清单 15-9 中看到的,您删除了包含单选按钮的第二列,代之以一组<div>元素(如果您直接从这里开始,您可以复制整个清单,而不仅仅是粗体文本)。jQuery UI 会将它们转换成滑块(参见第二章)。

清单 15-9。ch15_04.html

<table>

<tr>

<td>

<div>

<ul>

<li><input name="dataSeries" value="data1" type="radio" checked />First series</li>

<li><input name="dataSeries" value="data2" type="radio" />Second series</li>

<li><input name="dataSeries" value="data3" type="radio" />Third series</li>

<li><input name="dataSeries" value="data4" type="radio" />Fourth series</li>

</ul>

</div>

</td>

<td>

<div id="red">

<div id="slider-text">

<div id="0">0</div>

<div id="1">255</div>

</div>

</div>

<div id="green">

<div id="slider-text">

<div id="0">0</div>

<div id="1">255</div>

</div>

</div>

<div id="blue">

<div id="slider-text">

<div id="0">0</div>

<div id="1">255</div>

</div>

</div>

</td>

</tr>

</table>

此外,您还使用slider-text id向每个滑块添加了两个数值。这些值只不过是用于显示三个滑块所覆盖的值范围(0–255)的最小值和最大值的标签。当您必须表示网页中每张幻灯片的比例时,这种方法非常有用。

现在让我们添加所有的 CSS 样式指令,以确保这些新控件可以在现有页面的上下文中正确显示(见清单 15-10)。

清单 15-10。ch15_04.html

<style>

...

#red, #green, #blue {

float: left;

margin: 15px;

left: 50px;

}

#red .ui-slider-range {

background: #ef2929;

}

#red .ui-slider-handle {

border-color: #ef2929;

}

#green .ui-slider-range {

background: #8ae234;

}

#green .ui-slider-handle {

border-color: #8ae234;

}

#blue .ui-slider-range {

background: #729fcf;

}

#blue .ui-slider-handle {

border-color: #729fcf;

}

#slider-text div {

font-family: "Verdana";

font-size: 10px;

position: relative;

left: 17px;

}

</style>

关于 JavaScript 中的代码部分,您只保留管理用于选择所需系列的单选按钮的部分,将其与处理 RGB 值的新代码部分集成,通过三个滑块进行调整,如清单 15-11 所示。然后,这三个 RGB 值通过适当的函数转换为十六进制数,并组合形成 HTML 颜色代码,用井号(#)表示,后跟六个十六进制字符('rrggbb'),其中每一对代表一个从 0 到 255 的值,转换为十六进制格式。

清单 15-11。ch15_04.html

$(document).ready(function(){

...

$("input[type=radio][name=dataSeries]").attr("checked", false);

$("input[type=radio][name=dataSeries][value=data1]").attr("checked", true);

$("input[type=radio][name=dataSeries]").change(function(){

var val = $(this).val();

plot1.series[0].data = dataSets[val];

plot1.series[0].renderer.shapeRenderer.strokeStyle = "#" + col;

plot1.replot();

});

var col = "4bb2c5";

function hexFromRGB(r, g, b) {

var hex = [

r.toString( 16 ),

g.toString( 16 ),

b.toString( 16 )

];

$.each( hex, function( nr, val ) {

if ( val.length === 1 ) {

hex[ nr ] = "0" + val;

}

});

return hex.join( "" ).toUpperCase();

};

$( "#red, #green, #blue" ).slider({

orientation: "vertical",

range: "min",

max: 255,

change: refreshPlot

});

// set col to default "#4bb2c5";

$( "#red" ).slider( "value", 255 );

$( "#green" ).slider( "value", 140 );

$( "#blue" ).slider( "value", 60 );

function refreshPlot() {

var r = $( "#red" ).slider( "value" );

var g = $( "#green" ).slider( "value" );

var b = $( "#blue" ).slider( "value" );

var col = hexFromRGB(r, g, b);

plot1.series[0].renderer.shapeRenderer.strokeStyle = "#" + col;

plot1.replot();

}

$("[id=0]").css('top','90px');

$("[id=1]").css('top','-20px');

});

清单 15-11 中的最后两行代码使用 jQuery css()函数将 CSS 样式分配给特定的 HTML 元素(参见第二章)。在所有带有id = 0id = 1的元素上进行选择,也就是说,<div>元素包含滑块刻度的标签。您可以设置 CSS top 属性,将每个刻度标签放置在相应滑块旁边的特定高度。

在图 15-4 中,用户可以通过三个滑块修改 RBG 值来决定要显示和更改的系列。

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

图 15-4。

A chart with three slider widgets added to adjust the RGB levels

使用复选框

在前面的例子中,用户只能从可以显示的系列中选择一个。然而,典型地,用户将希望能够决定哪些系列应该被显示,哪些不应该被显示,例如,选择同时显示两个或更多组。这需要处理同一组中的多种选择。为了让用户做出这种选择,你必须选择复选框。

通常,复选框被分组在一个列表中,由空框表示(见图 15-1 )。与单选按钮不同,这些控件不是互斥的,而是多项选择。因此,您可以选择它们所代表的全部、部分值,或者不选择任何值(而对于单选按钮,必须选择一个项目)

与单选按钮类似,每个系列都有一个复选框,如果复选框被选中,相应的系列将显示在图表中。然而,与单选按钮不同,复选框是相互独立的:它们的状态(选中或未选中)不会影响其他复选框的状态。

通常,当您有一个复选框列表时,添加两个具有“全部选中/取消选中”功能的按钮会非常有用,从而允许通过一次单击来选择/取消选择所有复选框。

使用前面的例子(见清单 15-9 到 15-11),数据集和选项设置是相同的;您唯一需要更改的是在$.jqplot()函数中传递的数据。在这种情况下,整个数据集将作为参数传递。

var plot1 = $.jqplot ('myChart', [dataSet.data1, dataSet.data2, dataSet.data3, dataSet.data4], options);

让我们删除包含先前控件(单选按钮、滑块)的表格,并用一个包含复选框的新表格来替换它,如清单 15-12 所示(如果你直接从这里开始,你可以复制整个列表而不考虑先前的控件)。此外,除了用于多个系列的四个控件之外,您还可以在末尾添加一个按钮来管理“全部选中/取消选中”功能

清单 15-12。ch15_03.html

<table>

<tr>

<td>

<div>

<ul>

<li><input name="data1" type="checkbox" checked />First series</li>

<li><input name="data2" type="checkbox" checked />Second series</li>

<li><input name="data3" type="checkbox" checked />Third series</li>

<li><input name="data4" type="checkbox" checked />Fourth series</li>

<li><input type="button" name="checkall" value="Uncheck All"></li>

</ul>

</div>

</td>

</tr>

</table>

与单选按钮一样,您必须添加 jQuery 方法来绑定这些控件发生的事件。首先,定义每个复选框的状态。正常情况下都应该检查。然后,定义五个 jQuery 方法,启用或禁用要表示的系列,然后强制 replot。

从代码中,您必须删除所有处理先前控件的行,并在它们的位置上,编写清单 15-13 中的方法。

清单 15-13。ch15_03.html

$("input[type=checkbox][name=data1]").change(function(){

if(this.checked){

plot1.series[0].data = dataSet.data1;

plot1.replot();

} else {

plot1.series[0].data = [];

plot1.replot();

}

});

$("input[type=checkbox][name=data2]").change(function(){

if(this.checked){

plot1.series[1].data = dataSet.data2;

plot1.replot();

} else {

plot1.series[1].data = [];

plot1.replot();

}

});

$("input[type=checkbox][name=data3]").change(function(){

if(this.checked){

plot1.series[2].data = dataSet.data3;

plot1.replot();

} else {

plot1.series[2].data = [];

plot1.replot();

}

});

$("input[type=checkbox][name=data4]").change(function(){

if(this.checked){

plot1.series[3].data = dataSet.data4;

plot1.replot();

} else {

plot1.series[3].data = [];

plot1.replot();

}

});

$("input[type=button][name=checkall]").click(function(){

if(this.value == "Check All"){

plot1.series[0].data = dataSet.data1;

plot1.series[1].data = dataSet.data2;

plot1.series[2].data = dataSet.data3;

plot1.series[3].data = dataSet.data4;

$("input[type=checkbox][name=data1]").prop("checked", true);

$("input[type=checkbox][name=data2]").prop("checked", true);

$("input[type=checkbox][name=data3]").prop("checked", true);

$("input[type=checkbox][name=data4]").prop("checked", true);

this.value = "Uncheck All";

plot1.replot();

} else {

plot1.series[0].data = [];

plot1.series[1].data = [];

plot1.series[2].data = [];

plot1.series[3].data = [];

$("input[type=checkbox][name=data1]").prop("checked", false);

$("input[type=checkbox][name=data2]").prop("checked", false);

$("input[type=checkbox][name=data3]").prop("checked", false);

$("input[type=checkbox][name=data4]").prop("checked", false);

this.value = "Check All";

plot1.replot();

}

});

如图 15-5 所示,用户现在可以选择他或她想要在图表中显示的系列。

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

图 15-5。

A custom legend with check boxes and a button

如果您单击标记为“全部取消选中”的按钮,所有复选框都将被取消选中,相应的系列将在绘图中隐藏。随后,该按钮将显示标签“全部选中”当这次点击它时,所有的复选框将被选中,相应的系列将显示在图表中。

最后一个例子中涵盖的特性与 EnhancedLegendRenderer 插件提供的图例非常相似(参见第 10 章中的“处理图例”一节)。在这种情况下,通过单击与系列相对应的彩色方块,您可以决定是否应该在图表中显示该系列。但是,这里您还添加了只需一次点击就可以选中和取消选中所有系列的可能性,而且这个功能目前还没有在插件中实现(尽管有人正在提议)。这是如何通过使用控件来扩展库提供的功能的另一个小例子。

摘要

在本章中,您已经看到了如何使用各种控件(如单选按钮、滑块和复选框)来增加图表的交互性。随着控件的引入,作为程序员,我们不再是唯一能够直接控制图表属性值的人;通过这样的控制,用户也能够做出适当的选择。

此外,您了解了如何将 jQuery UI 小部件与 jqPlot 库集成,并将这些小部件用作控件。在下一章中,您将通过使用 jQuery UI 小部件作为图表的容器来完成这个集成。这种组合极大地扩展了使用 jqPlot 库开发和表示图表的可能性。