JavaScript-图表入门指南-三-

144 阅读21分钟

JavaScript 图表入门指南(三)

原文:Beginning JavaScript Charts

协议:CC BY-NC-SA 4.0

七、为简单图表创建库

Abstract

作为这本书第一部分的结论,你将利用你目前所学的一切从头开始创建一个你自己的图书馆。这将是一个专门表示三种不同类型的图表的库,这三种不同类型的图表是:折线图、条形图和饼图。

作为这本书第一部分的结论,你将利用你目前所学的一切从头开始创建一个你自己的图书馆。这将是一个专门表示三种不同类型的图表的库,这三种不同类型的图表是:折线图、条形图和饼图。

您将开发的是一个非常简化的当前互联网上可用的 JavaScript 库模型。目的是帮助您理解这种图表表示专用库的机制。

通过在这个简单的例子中包含所有的步骤,您可以更好地了解这个类的库是如何工作的,即使对于复杂得多的例子也是如此。跟随数据的流动,从它们在 HTML 页面中的定义到它们在库中的处理,您将发现数据是如何被转换成图形元素以形成您最感兴趣的图表类型的。这个库的逐步实现将阐明为什么 jQuery 库是许多此类库的基础。由于它的功能,可以动态管理 HTML 页面的组件。该库还在许多参数的实现和管理中起着关键作用,这些参数将对所创建的图形元素的属性产生直接影响,从而在不修改代码的情况下表征不同的图表表示,每次都指定一个 JavaScript 对象,在该对象中将传递所有这些参数。

在前几章中,您已经开发了这里需要的代码。由于画布的上下文和 jQuery 提供的许多功能,您已经看到了如何迭代地管理数据,如何将数据转换成图形。这样,您创建了三种最常见的图表类型。您将使用您开发的代码来创建您的库,探索如何将其参数化,以便您可以决定表示哪个图表以及在调用库时如何表示,而无需修改代码。

创建库

首先,您需要定义您的新库,并将其包含在您的 web 页面中。为此,您创建一个包含myLibrary()函数定义的新文件。您将该文件另存为myLibrary.js;这将是你的库,只要你想把它包含在一个网页中,它就可以被重用(见清单 7-1)。

清单 7-1。myLibrary.js

function myLibrary(target, data, options){

//add the JavaScript code here

}

同时,您开始实现一个新的 web 页面,包括其中的myLibrary文件,如清单 7-2 所示。

清单 7-2。ch7_01.html

<HTML>

<HEAD>

<TITLE>MyChart</TITLE>

</HEAD>

<BODY>

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

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

<script>

$(document).ready(function(){

// add data and options here

myLibrary("#myCanvas", data, options);

});

</script>

<canvas id="myCanvas" width="500" height="400"> </canvas>

</BODY>

</HTML>

正如您所看到的,首先您已经在 web 页面中包含了 jQuery 库,这样您就可以在使用代码时利用这个库提供的所有方法。

Note

如果您希望将 jQuery 库包含在内容交付网络(CDN)服务中,您需要使用以下参考资料:

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

有关工作区和库路径的更多信息,请参见附录 a。

主要功能:目标、数据和选项

$(document).ready()函数中,你用myLibrary()函数调用你的库。在这个调用中,您传递了三个不同的参数:

  • target
  • data
  • options

你会发现这种类型的调用在很多库中都很常见,包括 jqPlot 和 Highcharts。因此,在实现您的库时,您已经开始处理将构成后续章节基础的概念。

  • target是画布的 ID 类的名称。target被传递到库中,使您能够定义一个上下文并在其中绘制所有需要的图形。您可以在同一个网页中使用不同的画布,每个画布代表一种不同的图表类型,但是必须通过给target起不同的名字来区分所有画布。
  • data是包含输入数据的数组。在前面的章节中,您使用了包含在表中的数据,并通过您实现的解析器将它提取出来。这有助于您理解 jQuery 的潜力,但是,事实上,大多数时候这些输入值可以有任何来源并采用任何形式。通常,转换成可读格式不是库的工作,而是其他支持代码的工作。因此,对于您的库,输入格式必须是数组。
  • options是一种对象数据类型,也可以采用复杂的结构,为此你需要指定一些与属性值相关的属性。您将使用这种类型的结构将一系列参数传递给库,以描述图表的所有图形组件。基本上,这需要定义一套关于你希望你的库如何表示你的图表的指导方针。

一旦你熟悉了这些基本概念,你会发现这本书涵盖的所有库,以及互联网上的其他库,将会更容易理解和使用。

与所有其他库一样,您正在实现的库将接受数组形式的输入数据。您不用实现解析器,解析器从 HTML 表中提取值,而是以数字数组的形式直接写入数据。在调用myLibrary()函数之前,data变量将在$(document).ready()中定义(见清单 7-3)。

清单 7-3。ch7_01.html

$(document).ready(function(){

var data = [[12, 40, 75, 23, 42, 80],

[3, 22, 40, 27, 35, 21],

[60, 80, 16, 28, 33, 26],

[46, 7, 14, 26, 36, 24]];

myLibrary("#myCanvas",data,options);

});

如前所述,这些是 HTML 表格的数值(见图 7-1 )。但是,标题、月份和国家名称发生了什么变化呢?您也将把这两组值表示为一个数组,但是您没有将它们作为输入数据引入,而是将它们用作通过options插入的图表的属性。

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

图 7-1。

The input data can come from any kind of source (e.g., a table); the important thing to keep the data structure

您可以考虑一年中的月份刻度标签(也称为类别),但是对于国家的名称,它们只不过是一系列值的名称,这些值将在图例中报告,并且将被分配不同的颜色。事实上,大多数标签将被分配给图表的组件,所以最好通过options传递它们(见清单 7-4)。以前,您通过数组定义了一个颜色序列。因此,您也将在options中传递这个数组。

清单 7-4。ch7_01.html

$(document).ready(function(){

var data = [[12, 40, 75, 23, 42, 80],

[3, 22, 40, 27, 35, 21],

[60, 80, 16, 28, 33, 26],

[46, 7, 14, 26, 36, 24]];

var options = {

categories: ["May 2012", "Jun 2012", "Jul 2012",

"Aug 2012", "Sep 2012", "Oct 2012"],

series: ["USA","Canada","Australia", "Brazil"],

colors: ['#be1e2d', '#666699', '#92d5ea', '#ee8310'],

};

myLibrary("#myCanvas",data,options);

});

但是,你忘了最重要的一点。您想使用哪种类型的图表?您也将在options中指定这个信息,在这个特殊的例子中,定义一个具有三个可能值的type属性:

  • line
  • bar
  • pie

就图表中可以参数化的内容而言,这只是冰山一角。在实现你的库时,任何表征图形元素外观或功能的参数都可以通过options对象在外部设置,如清单 7-5 所示。

清单 7-5。ch7_01.html

$(document).ready(function(){

var data = [[12, 40, 75, 23, 42, 80],

[3, 22, 40, 27, 35, 21],

[60, 80, 16, 28, 33, 26],

[46, 7, 14, 26, 36, 24]];

var options = {

//type: 'line',

type: 'bar',

//type: 'pie',

categories: ["May 2012", "Jun 2012", "Jul 2012",

"Aug 2012", "Sep 2012", "Oct 2012"],

series: ["USA","Canada","Australia", "Brazil"],

colors: ['#be1e2d', '#666699', '#92d5ea', '#ee8310'],

};

myLibrary("#myCanvas",data,options);

});

这里,我们只拿几个小例子,因为我们的目的是说明性的;重要的是理解基本的方法论。例如,在前面的例子中定义画布时,您在绘图区域中指定了边距。然而,在这种情况下,让用户在库之外直接定义这些参数更合适。

在其他情况下,甚至可能有更具体的参数,典型的单一类型的图表。在这种情况下,您将有一个进一步的嵌套结构,例如一个options对象在另一个options对象内,该对象只特定于一种类型的图表。例如,这种方法是 jqPlot 中构成options对象的大量属性和子属性的基础,jqPlot 是一个库,您将在本书的下一部分研究它。因此,举例来说,让我们将barGroupMargin属性作为特定参数插入条形图(见图 7-2 )。使用此属性,您可以控制条形之间的距离。因为该属性只针对一种图表类型,所以它将在一个bar对象中指定,该对象又包含在options中。

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

图 7-2。

Setting the barGroupMargin property, you can modify the distance between the bars

甚至画布的边距也可以定义为options中的属性。这样,您可以调整图表的位置,而无需每次都更改myLibrary.js库中的代码。

使用这种方法,根据options层次中的影响区域,细分属性,将它们分配给描述该区域的对象(见图 7-3 )。

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

图 7-3。

The hierarchy of the options object reflects the hierarchy of the elements that form the chart

在这种情况下,在options对象中有许多属性需要设置,如清单 7-6 所示。

清单 7-6。ch7_01.html

$(document).ready(function(){

var data = [[12, 40, 75, 23, 42, 80],

[3, 22, 40, 27, 35, 21],

[60, 80, 16, 28, 33, 26],

[46, 7, 14, 26, 36, 24]];

var options = {

//type: 'line',

type: 'bar',

//type: 'pie',

categories: ["May 2012", "Jun 2012", "Jul 2012",

"Aug 2012", "Sep 2012", "Oct 2012"],

series: ["USA","Canada","Australia", "Brazil"],

colors: ['#be1e2d', '#666699', '#92d5ea', '#ee8310'],

margins: {top: 30, right: 10, bottom: 10, left: 30},

bar: {

barGroupMargin: 4

}

};

myLibrary("#myCanvas",data,options);

});

这样,您就完成了对网页中所有要定义的内容的定义。现在,必须使用data数组处理输入数据并将其转换成图形元素。在您的options对象中,您还有一系列参数来描述图表的特征。最后,您已经指出了target,即您将在其上绘制图表的画布。所以,让我们看看myLibrary内部来解决所有这些问题。

实现库

现在您已经完成了 web 页面中所有内容的定义,您必须开始实现您的库。如果您回过头来看看用来获得折线图、条形图和饼图的代码,您会发现这些代码有许多共同点。正是这些公共部分构成了库的主干,而那些特定于图表类型的部分将在一个if()语句中单独实现,该语句只有在options中选择的类型对应时才会激活。

设置画布

代码的一个公共部分是应用于画布的上下文的定义,如清单 7-7 所示。

清单 7-7。myLibrary.js

function myLibrary(target,data,options){

var canvas = $(target);

var margin = options.margins;

var w = canvas.width() - margin.left - margin.right,

h = canvas.height() - margin.top - margin.bottom;

var ctx = canvas.get(0).getContext("2d");

if(options.type === 'pie'){

ctx.strokeRect(margin.left, margin.top, w, h);

} else {

ctx.translate( 0, canvas.height() );

ctx.strokeRect(margin.left, -margin.bottom, w, -h);

}

};

如您所见,这里使用了target参数。关于边距的定义,您必须记住在options对象中定义它们,因此您将读取内部定义的值。获取这些值真的很简单;您只需在每次需要时定义语句:

options. property

因此,边距将使用options.margins。在这部分代码中,三种图表之间的唯一区别是定义设计区域的矩形,其方向与饼图的页面一致,而对于折线图和条形图,方向相反。这就是为什么只有当options.type不同于'pie'时才应用ctx.translate()

绘制轴、记号标签和网格

现在,让我们将代码添加到您的库中。这段代码处理 x 和 y 轴刻度标签的创建。只有折线图和条形图才需要这些组件,因为它们是在笛卡尔轴上表示的;饼图不使用它们。因此,您在if()语句中应用条件,以便只为这两种类型的图表执行代码。你正在实现的代码与前几章中使用的相同,除了在这种情况下,你已经用options对象和data数组中可用的其他变量替换了变量(见清单 7-8)。

清单 7-8。myLibrary.js

function myLibrary(target,data,options){

...

var ctx = canvas.get(0).getContext("2d");

if(options.type === 'pie'){

ctx.strokeRect(margin.left, margin.top, w, h);

} else {

ctx.translate( 0, canvas.height() );

ctx.strokeRect(margin.left, -margin.bottom, w, -h);

}

if(options.type === 'line' || options.type === 'bar'){

var minVal = 0;

var maxVal = 0;

data.forEach(function(d,i){

var min = Math.min.apply(null, d);

if(min < minVal)

minVal = min;

var max = Math.max.apply(null, d);

if(max > maxVal)

maxVal = max;

});

maxVal = 1.1 * maxVal;

//calculate yLabels

var yLabels = [];

var yDeltaPixels = 30;

var nTicks = Math.round(h / yDeltaPixels);

var yRange = maxVal - minVal;

var yDelta = Math.ceil(yRange / nTicks);

var yVal = minVal;

while(yVal < (maxVal - yDelta)){

yLabels.push(yVal);

yVal += yDelta;

}

yLabels.push(yVal);

yLabels.push(maxVal);

//draw xLabels

if(options.type === 'line'){

var xDelta = w / (options.categories.length - 1);

}

if(options.type === 'bar'){

var xDelta = w / (options.categories.length);

}

var xlabelsUL = $('<ul class="labels-x"></ul>')

.width(w)

.height(h)

.insertBefore(canvas);

$.each(options.categories, function(i){

var thisLi = $('<li><span class="label">' + this + '</span></li>')

.prepend('<span class="line" />')

.css('left', xDelta * i)

.width(0)

.appendTo(xlabelsUL);

var label = thisLi.find('span.label');

});

//draw yLabels

var yScale = h / yRange;

var liBottom = h / (yLabels.length-1);

var ylabelsUL = $('<ul class="labels-y"></ul>')

.width(w)

.height(h)

.insertBefore(canvas);

$.each(yLabels, function(i){

var thisLi = $('<li><span>' + this + '</span></li>')

.prepend('<span class="line"  />')

.css('bottom', liBottom * i)

.prependTo(ylabelsUL);

var label = thisLi.find('span:not(.line)');

var topOffset = label.height()/-2;

if(i == 0){ topOffset = -label.height(); }

else if(i== yLabels.length-1){ topOffset = 0; }

label

.css('margin-top', topOffset)

.addClass('label');

});

}

};

如果你看一下前面章节中定义的级联样式表(CSS)样式,对于三种类型的图表,你会发现它们并不完全相同,特别是对于某些类别的样式。为了克服这个问题,最简单的方法是定义这些属性,在定义它们的时候,对各种类型的样式(或者说,代表它们的标签)使用css()函数。因此,对于所有三种类型的图表,您可以使用相同名称的样式类,但是使用不同的值,因为它们都有自己的css()函数。

例如,当您定义管理 x 轴刻度标签的 CSS 类span.label时,根据您是在处理折线图还是条形图,这些标签的行为必须不同。如果您想要表示折线图,将在刻度处报告刻度标签,但是如果您想要表示条形图,应该在两个刻度处报告标签。因此,您必须以不同的方式定义同一个span.label类的属性,您可以通过添加 40 个像素的左边距和条形图独有的css()函数来实现。代码的相关部分如清单 7-9 所示。

清单 7-9。myLibrary.js

function myLibrary(target,data,options){

...

if(options.type === 'line' || options.type === 'bar'){

...

$.each(options.categories, function(i){

var thisLi = $('<li><span class="label">' + this + '</span></li>')

.prepend('<span class="line" />')

.css('left', xDelta * i)

.width(0)

.appendTo(xlabelsUL);

var label = thisLi.find('span.label');

if(options.type === 'line'){

label.addClass('label');

}

if(options.type === 'bar'){

label.css('margin-left', '40px')

.addClass('label');

}

});

//draw yLabels

var yScale = h / yRange;

var liBottom = h / (yLabels.length-1);

...

}

};

因为我们正在讨论 CSS 类,所以让我们把你在前面章节中使用的所有定义添加到你的网页中,这些定义对所有三种类型都有效。但是,不要把它们直接添加到你的网页上,把它们写在<style></style>标签之间,你必须把这些 CSS 定义看作是库的一部分;因此,最好将它们写在一个新的 CSS 文件中,你将称之为myLibrary.css(见清单 7-10)。

清单 7-10。myLibrary.css

canvas {

position: relative;

}

ul,.li {

margin: 0;

padding: 0;

}

.labels-x, .labels-y {

position: absolute;

left: 37;

top: 37;

list-style: none;

}

.labels-x li {

position: absolute;

bottom: 0;

height: 100%;

}

.labels-x li span.label {

position: absolute;

color: #555;

top: 100%;

margin-top: 5px;

left:-15;

}

.labels-x li span.line{

position: absolute;

border: 0 solid #ccc;

border-left-width: 1px;

height: 100%;

}

.labels-y li {

position: absolute;

bottom: 0;

width: 100%;

}

.labels-y li span.label {

position: absolute;

color: #555;

right: 100%;

margin-right: 5px;

width: 100px;

text-align: right;

}

.labels-y li span.line {

position: absolute;

border: 0 solid #ccc;

border-top-width: 1px;

width: 100%;

}

.legend {

list-style: none;

position: absolute;

left: 520px;

top: 40px;

border: 1px solid #000;

padding: 10px;

}

.legend li span {

width: 12px;

height: 12px;

float: left;

margin: 3px;

}

.chart-title {

font-size: 24;

font-weight: bold;

position: absolute;

left: 150px;

top: 10px;

width: 100%

}

为了使这些 CSS 样式设置有效,新的 CSS 文件必须包含在你的网页中,并带有一个指向该文件的链接,如清单 7-11 所示。

清单 7-11。myLibrary.js

<HEAD>

<TITLE>MyChart</TITLE>

</HEAD>

<BODY>

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

<link href="./myLibrary.css" rel="stylesheet" type="text/css">

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

<script>

...

绘图日期

现在你必须使用画布的上下文来定义将输入数据转换成图形元素的代码部分(见清单 7-12)。这部分特定于每种类型的图表,因此每种图表都有不同的实现。

清单 7-12。myLibrary.js

function myLibrary(target,data,options){

...

if(options.type === 'line' || options.type === 'bar'){

...

}

if(options.type === 'line'){

// draw DATA

ctx.lineWidth = 5;

for(var i in data){

var points = data[i];

ctx.moveTo(0,-points[i]);

ctx.strokeStyle = options.colors[i];

ctx.beginPath();

var xVal = margin.left;

for(var j in points){

var relY = (points[j] * h / maxVal) + 10;

ctx.lineTo(xVal,-relY);

xVal += xDelta;

}

ctx.stroke();

ctx.closePath();

}

} // end of LINE

if(options.type === 'bar'){

var barGroupMargin = options.bar.barGroupMargin;

for(var i in data){

ctx.beginPath();

var n = data.length;

var lineWidth = (xDelta - barGroupMargin * 2) / n;

var strokeWidth = lineWidth - (barGroupMargin * 2);

ctx.lineWidth = strokeWidth;

var points = data[i];

var xVal = (xDelta - n * strokeWidth - (n - 1) * (lineWidth - strokeWidth)) / 2;

for(var j in points){

var relX = margin.left + (xVal - barGroupMargin) +

(i * lineWidth) + lineWidth / 2;

ctx.moveTo(relX,-margin.bottom);

var relY = margin.bottom + points[j] * h / maxVal;

ctx.lineTo(relX, -relY);

xVal += xDelta;

}

ctx.strokeStyle = options.colors[i];

ctx.stroke();

ctx.closePath();

}

} // end of bar

if(options.type === 'pie'){

var pieMargin = margin.top + 50;

var centerx = Math.round(w / 2) + margin.left;

var centery = Math.round(h / 2) + margin.top;

var radius = centery - pieMargin;

var counter = 0.0;

var dataSum = function(){

var dataSum = 0;

for(var i in data){

var points = data[i];

for(var j in points){

dataSum += points[j];

}

}

return dataSum;

}

var dataSum = dataSum();

var labels = $('<ul class="labels"></ul>')

.css('list-style','none')

.insertBefore(canvas);

for(var i in data){

var sum = 0;

var points = data[i];

for(var j in points){

sum += points[j];

}

var fraction = sum / dataSum;

ctx.beginPath();

ctx.moveTo(centerx, centery);

ctx.arc(centerx, centery, radius,

counter * Math.PI * 2 - Math.PI * 0.5,

(counter + fraction) * Math.PI * 2 - Math.PI * 0.5, false);

ctx.lineTo(centerx, centery);

ctx.closePath();

ctx.fillStyle = options.colors[i];

ctx.fill();

var sliceMiddle = (counter + fraction / 2);

var distance = radius * 1.2;

var labelx = Math.round(centerx +

Math.sin(sliceMiddle * Math.PI * 2) * (distance));

var labely = Math.round(centery -

Math.cos(sliceMiddle * Math.PI * 2) * (distance));

var leftPlus = (labelx < centerx) ? '40' : '0' ;

var percentage = parseFloat((fraction * 100).toFixed(2));

var labelval = percentage + "%";

var labeltext = $('<span class="label">' + labelval +'</span>')

.css('font-size', radius / 8)

.css('color', options.colors[i])

.css('font-weight', 'bold');

var label = $('<li class="label-pos"></li>')

.appendTo(labels)

.css({left: labelx-leftPlus, top: labely, position: 'absolute',

padding: 0})

append(labeltext);

counter+=fraction;

}

} //end of pie

};

添加图例

要在库中定义的最后一部分代码是实现图例组件的代码(见清单 7-13)。因为这一部分对于所有三种类型的图表都是相同的,所以它不受if()语句的约束。注意,在代码中,系列由options.series读取,颜色由options.colors数组读取。

清单 7-13。myLibrary.js

function myLibrary(target,data,options){

...

if(options.type === 'pie'){

...

} //end of pie

//draw the legend

var legendList = $('<ul class="legend"></ul>')

.insertBefore(canvas);

for(var i in options.series){

$('<li>'+ options.series[i] +'</li>')

.prepend('<span style="background: '+ options.colors[i] +'" ></span>')

.appendTo(legendList);

}

};

如果您将三个值'pie''line''bar'分配给type属性,您将得到与前面章节中相同的三个图表,除了生成它们的代码被压缩成一个唯一的版本:库myLibrary.js。此外,您现在拥有了能够从库外部配置一切的优势。

默认值

但是,假设您忘记了在options中定义一个参数。接下来会发生什么?您启动的页面肯定无法正常工作。事实上,所有的库,包括非常简单的库,都必须包含在options中定义的所有值,而这些值必须已经定义了默认值。这是一个非常重要的概念,你会在 jqPlot 和 Highcharts 库中看到它。

你已经知道所有的图形元素都可以用标准参数来描述,这些参数一个接一个地创建了一个树形结构属性,你可以在options对象中找到。任何一个库,无论多么简单或复杂,都有这样的内部结构。实际上,每个库都必须提供它的options结构,其中每个属性都已经指定了一个默认值,所以如果这个属性没有在options对象中声明并作为参数传递给myLibrary()函数,您不会得到任何错误,因为一个值已经被赋给了那个属性。然而,这样做的原因不仅仅是为了确保当您忘记输入一个值时您的库能够运行,而是为了以最小的努力获得最大的结果。想象一个比您刚刚实现的库复杂得多的库,其中要定义的属性有好几个。这种类型的库很可能是 jqPlot。正如您将看到的,您只需要定义几行代码就可以获得很好的结果。事实上,只写你想改变的参数就足够了;这为你节省了大量的时间和精力。

为了更好地理解这个概念,如果你不希望在属性barGroupMargin中定义一个值,例如,因为它的缺省值 4 适合你的需要,那么你不需要在options对象中写任何对它的引用,如清单 7-14 所示。

清单 7-14。ch7_01b.html

var options = {

type: 'bar',

categories: ["May 2012", "Jun 2012", "Jul 2012",

"Aug 2012", "Sep 2012", "Oct 2012"],

series: ["USA", "Canada", "Australia", "Brazil"],

colors: ['#be1e2d', '#666699', '#92d5ea', '#ee8310'],

margins: {top: 30, right: 10, bottom: 10, left: 30},

bar: {}

}

并且,这个库,经过适当的修改来处理这个值的缺失,分配了缺省值 4(见清单 7-15)。

清单 7-15。myLibrary.js

function myLibrary(target,data,options){

...

if(options.type === 'line'){

...

} // end of LINE

if(options.type === 'bar'){

if(typeof options.bar.barGroupMargin!= 'undefined') {

var barGroupMargin = options.bar.barGroupMargin;

} else {

var barGroupMargin = 4;

}

for(var i in data){

ctx.beginPath();

var n = data.length;

...

} // end of bar

...

};

图 7-4 显示了你在前三章中实现的三种类型的图表,但是这次生成它们的代码都在一个文件中。

A978-1-4302-6290-9_7_Fig4a_HTML.jpgA978-1-4302-6290-9_7_Fig4b_HTML.jpg

图 7-4。

The library generates three type of charts: (a) a line chart, (b) a bar chart, and (c) a pie chart

摘要

读到这一章,你就完成了这本书的第一部分。您已经看到了如何用 JavaScript 创建一个特定于图表数据表示的库。

学习重用已经实现的代码来开发你自己的库,你已经开始理解这种类型的库是如何构造的,以及各部分执行的功能是什么。特别重要的是对树结构的介绍,我们称之为options,许多库都有。options对象在定义图形组件的所有设置中起着重要的作用,因此也定义了如何表示图表。

此外,您还看到了如何通过输入数组在内部管理这类库的数据,jQuery 库所扮演的角色,以及在其上构建数据的原因。

在下一章,你将开始这本书的第二部分,在这一部分中,jqPlot 和 Highcharts 库将被完整地讨论。这些库在 web 开发人员中取得了一些成功。尽管它们比你刚刚开发的库更复杂,功能更丰富,但有了它们,你会发现本章涵盖的所有概念。

八、jqPlot 简介

Abstract

从这一章开始,你将开始本书的第二部分,关于 jqPlot 库。在本章的课程中,你将会被介绍到这个库的基本概念。在了解了库的结构和组成库的文件之后,您将开始理解只使用几行代码制作图表是多么容易。

从这一章开始,你将开始本书的第二部分,关于 jqPlot 库。在本章的课程中,你将会被介绍到这个库的基本概念。在了解了库的结构和组成库的文件之后,您将开始理解只使用几行代码制作图表是多么容易。

通过一系列的例子,并通过插件的使用,你将逐渐学会如何表示任何类型的图表。一切都将使用$.jqPlot()函数来完成,它的三个参数描述了 jqPlot 库的所有特性:目标画布、输入数据数组和 options 对象。

最后,在简要说明了如何通过使用级联样式表(CSS)样式定制图表之后,您将快速了解模块思维如何使您的实现有序、可维护和可重用。因此,让我们开始介绍这个奇妙的图书馆。

jqPlot 库

jqPlot 是一个 JavaScript 库,专门用于在网页中生成图表。jqPlot 完全用纯 JavaScript 编写,是一个开源项目,自 2009 年以来由 Chris Leonello 完全开发和维护。当扩展时,jQuery 库达到了它的全部潜在功能。正是由于这个原因,除了它的简单性,jqPlot 是当今最流行的图表表示库之一。

jqPlot 非常成功,几乎取代了其他以前的库,比如 Flot,jqPlot 保留了它的许多方面,包括外观和感觉。事实上,jqPlot 的作者经常承认他是 Flot 的忠实用户,但是随着时间的推移,他开始意识到它的局限性。旧图书馆缺乏许多功能;此外,其架构的构建方式使其难以扩展。因此,Leonello 觉得有必要创建一个新的图书馆,保留 Flot 中所有好的东西,但允许它发展。因此,他完全重写了它的架构。jqPlot 具有高度模块化的结构,正如您将看到的,它基于大量的插件,每个插件都扮演一定的角色。因此,它最大的特点是它的可插拔性。用户绘制的每一个对象,无论是线条、轴、阴影还是网格本身,都由一个插件来处理。每个绘图元素都有可定制的设置选项,每个添加的插件都可以扩展绘图的功能。

插件的数量逐渐增加,进一步扩大了库的目标。jqPlot 现在是一个多功能和可扩展的库,适合那些想在几个步骤中开发专业图表的人。

在大多数情况下,jqPlot 允许您绘制漂亮的图表,而无需添加太多行代码。事实上,您将会看到 jqPlot(也许比 jQuery 更好)已经接受了“少写多做”的理念。我认为这是图书馆最受赞赏的方面。每天都有越来越多的开发者被添加到 jqPlot 用户列表中。

包括基本档案

当您决定利用 jqPlot 在您的网站上绘制图表时,首先需要包含一组关键文件。

如前所述,jqPlot 本质上是 jQuery 的扩展,因此使用它需要包含 jQuery 插件(见表 8-1 )。您可以从 jqPlot 官方网站( www.jqplot.com )下载这个插件,以及组成 jqPlot 库的所有其他插件,包括 CSS 文件。根据发布版本的不同,这些文件被分组到不同的发行版中。

Note

本书中的所有示例都使用 jqPlot 库的 1.0.8 版本。

表 8-1。

The distributions of jqPlot and versions of jQuery on which they are based

| jqbatch 版本 | jQuery 版本 | | --- | --- | | 1.0.6–1.0.8 | 1.9.1 | | 1.0.2–1.0.5 | 1.6.4 |

但是,有一小部分文件代表了库的核心,如果您想包含 jqPlot 提供的所有功能,这些文件是必不可少的。这组基本文件由 jQuery 插件、jqPlot 插件和一个 jqPlot CSS 文件组成。还有另一个文件需要导入,但只有当您希望在低于版本 9 的 Internet Explorer 浏览器中加载页面时才需要导入:ExplorerCanvas (excanvas)脚本。这个可选文件弥补了 HTML5 引入的画布功能的不足。

因此,在您的 web 页面的<head></head>标记中,您将包含这些文件(有关如何设置工作区的更多信息,请参见附录 A):

<!--[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" />

除了在本地使用 jqPlot 库,通过从网站下载,您还可以使用内容交付网络(CDN)服务,就像您使用 jQuery 一样。jsDelivr ( www.jsdelivr.com/#!jqplot )是一个 CDN 网站,提供 jqPlot 的所有最新发行版。如果您想使用此服务,可以按如下方式修改 URL:

<!--[if lt IE 9]><script type="text/javascript" src="http://cdn.jsdelivr.net/excanvas/r3/excanvas.js]-->>

<script src="http://code.jquery.com/jquery-1.9.1.min.js>>

<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/jquery.jqplot.min.js

<link rel="stylesheet" type="text/css" href="http://cdn.jsdelivr.net/jqplot/1.0.8/jquery.jqplot.min.css

您很快就会发现,在网络中,您会遇到类似的文件,有些带有缩写“min”(表示“minified”)。您应该总是尝试使用最小版本,这些文件的压缩版本。它们加载速度更快。只有当您打算在内部修改这些库时,才应该使用它们的普通版本(不带“min”)。

绘图基础

现在您已经看到了 jqPlot 库是如何构造的,并且查看了操作它所需的文件集,您可以开始学习如何在您的 web 页面中使用这些文件。在图表开发中,有两个基本步骤:创建一个表示图表的区域,并插入一个 JavaScript 代码部分,该代码需要调用 jqPlot 库提供的所有函数、变量和一系列对象。

添加绘图容器

在第 3-7 章中,您看到了画布是如何被用作开发图表的绘图区域的。类似地,jqPlot 库需要在 HTML 页面的<body>部分定义一个容器(一个元素)。这个容器将作为库的画布。

在本章中,该容器被称为目标。在一个网页中,每个目标(在本例中是绘图容器)由一个特定的id标识。在本书中,您将总是发现myChart作为目标的标识符(,但是它可以采用任何名称,并且您必须始终记住,一个以上的目标可能被分配给同一个 web 页面。此外,指定目标的宽度和高度也很重要。这些将定义网页中绘图区域的大小(见清单 8-1)。

清单 8-1。ch8_01.html

<BODY>

...

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

...

</BODY>

创造情节

要输入,jqPlot 命令和几乎所有的 JavaScript 代码都必须包含在一个<scripts>标签中。但是,一个网页分为两部分:头部和身体。那么,插入 JavaScript 代码的最佳位置是哪里呢?虽然可以将代码放在库的两个部分中,但是最好只放在一个部分中,这取决于库。考虑到 jqPlot 的工作方式,您将把代码放在<head></head>标记之间。

此外,jqPlot 是 jQuery 的扩展,所以如果你想让你的代码被执行,你需要在$(document).ready()函数中调用它的所有方法(见清单 8-2)。

清单 8-2。ch8_01.html

$(document).ready(function(){

// Insert all jqPlot code here.

});

然后,为了创建实际的绘图,您必须用您想要在其中绘制图表的目标的id调用$.jqplot插件。这个调用由下面的 jQuery 函数执行:

$.jqplot(``target, data, options

jqplot()函数有三个参数;target,它是要在其中渲染绘图的目标元素的 ID—在绘图容器中指定的 ID 属性;数据,由数据序列数组组成;和选项,jqPlot 的主要特性。在选项中,您将输入必要的自定义设置,使您的图表更适合您的需求和口味。

如果没有定义任何选项(是的,是可选的!),您可以按照标准选项的设置生成图表。事实上,已经定义了许多选项,没有必要在每次开发图表时都更改所有设置。如果标准选项符合您的要求,则不需要定义它们。这将为您节省大量时间,并避免编写多行代码。例如,让我们编写清单 8-3 中的函数。

清单 8-3。ch8_01.html

$(document).ready(function(){

$.jqplot ('myChart', [[100, 110, 140, 130, 80, 75, 120, 130, 100]]);

});

只需几行代码,您就可以生成如图 8-1 所示的图表。

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

图 8-1。

A line chart created with only a few lines of code

根据您刚才看到的内容,您可以推测,如果您不指定任何选项,默认的结果将是一个折线图,并且您添加的数据将被解释为折线图。因此,数组中的值是 y 值,它们序列的索引在 x 轴上报告。在后面的章节中,你将学习如何解释这些值,以及如何从线性图表中得到不同类型的图表。

使用 jqPlot 插件

最新的 jqPlot 发行版提供了大约 30 个插件(有关所有 jqPlot 插件的列表,请参见附录 B)。每一个都专门执行一个特定的任务,其名称通常表示功能。在接下来的章节中,您将会看到许多这样的插件——它们的用途和主要选项。

让我们以 BarRenderer 为例。如果您希望将输入数据解释为条形图,则此插件是必需的:

$.jqplot ('myChart', [[100, 110, 140, 130, 80, 75, 120, 130, 100]],

{

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

});

在 jqPlot 中,我们经常将插件称为渲染器。这是因为框架的架构指定每个插件必须覆盖一个特定的任务。如果开发人员认为有必要,那么他或她会包含它。此外,真正的渲染器应该尽可能地相互独立。事实上,您可以添加任意数量的插件,通常它们的顺序并不重要。有些插件不需要您指定任何额外的选项或设置;它们已经被定义,并且仅仅由于被包含而被直接激活。一个这样的插件是 Highlighter,它突出显示鼠标附近的数据点。但是,如果您对默认设置不满意,您总是可以用新值定义属性;这些插件还包含其他可设置的属性。其他插件提供的功能必须在 options 参数中指定才能被激活。

因此,jqPlot 库的基本元素和附加组件(由包含的插件逐渐引入)都可以通过一系列属性来表征(与 CSS 样式非常相似)。jqPlot 库调用这些属性选项。

了解 jqPlot 选项

有效使用 jqPlot 的关键是理解 jqPlot 的选项。图表中任何对象的属性都是由属性定义的,这些属性可以取不同的值。理解如何通过我将称为选项的对象类型来设置和使用这些属性是非常重要的。

插入选项

到目前为止,您已经看到了如何在 JavaScript 代码中调用jqPlot()函数,以及如何包含插件和数据,但是您还没有观察到如何输入选项。您可以通过向$.jqplot()函数传递不同的属性来自定义默认折线图,如下所示:

$(document).ready(function(){

$.jqplot ('myChart', [[100, 110, 140, 130, 80, 75, 120, 130, 100]],

{

//All the attributes here.

});

});

首先要注意的是,在调用$.jqplot()之后,不能直接在图表对象中设置属性。充其量,这不会做任何事情。您必须传递选项参数中的所有属性。

options 参数表示每个属性中的jqPlot对象。图表的所有特征都由许多属性表示,这些属性被设置为特定的值。这些值将条形图与折线图区分开,控制线条的笔划或轴的长度,指示是否显示图例以及在哪里显示,等等。通常,当包含各种插件时,没有必要指定所有属性的值;它们已经被设置为默认值。正是因为这些缺省值,在添加插件时,您无需添加一行代码就可以实现一个漂亮的图表。如果显式指定属性,实际上是覆盖了已经用默认值定义的属性的值。

因为我们的目标是设置jqPlot对象,并且因为它是由一系列组件组成的,所以有必要通过定义一系列对象及其属性来构建一个能够完美反映这些组件的options对象。调用与组件相对应的对象,并为其在options对象中的一个属性赋值,您将覆盖默认值并更改jqPlot对象的相应组件的属性。您可以在options对象中定义的最常用的对象有

  • seriesColors
  • stackSeries
  • title
  • axesDefaults
  • axes
  • seriesDefaults
  • series
  • legend
  • grid
  • cursor
  • highlighter

每个名称都反映了将受属性值更改影响的图表组件。这些对象是由一系列定义明确的属性构建的,每个属性都有自己的默认值。

这是jqPlot对象的结构:

jqplot 对象>组件对象>对象属性>默认值

在清单 8-4 中,你可以看到在定义选项对象时需要遵循的相应结构。

清单 8-4。ch8_02c.html

var options = {

axes:{

yaxis:{

min: 70,

max: 150

},

...

},

...

};

我认为最容易添加到图表中的对象是title。它不包含任何属性,通常被认为是jqPlot对象的属性本身。此外,可以直接在上面设置一个文本值,该文本将成为图表的标题。鉴于其简单性,title是理解如何使用选项的一个很好的起点(清单 8-5)。

清单 8-5。ch8_02a.html

$(document).ready(function(){

$.jqplot ('myChart', [[100, 110, 140, 130, 80, 75, 120, 130, 100]],

{

title: 'My first jqPlot chart'

});

});

如果你愿意,你也可以用jqplot()函数从外部定义对象的属性,将属性分配给一个变量。然后这个变量将作为一个参数在jqPlot()函数中传递,如清单 8-6 所示。这个变量实际上是options对象。

清单 8-6。ch8_02a.html

var options = { title: 'My first jqPlot chart' };

$.jqplot ('myChart', [[100, 110, 140, 130, 80, 75, 120, 130, 100]], options);

在这两种情况下,您现在都有一个顶部带有标题的图表(见图 8-2 )。

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

图 8-2。

Adding a title to a line chart

为了更好地理解如何在options对象中设置 jqPlot 属性,让我们举个例子,参考 jqPlot 网站的 API 文档部分( www.jqplot.com/docs/files/jqplot-core-js.html )。假设您想要隐藏图表中的网格线。在属于grid对象的属性列表中,您将找到您正在寻找的内容:

this.drawGridlines = true.

this是网格的实例,true是在创建jqplot对象时分配给它的默认值。因为您希望隐藏网格(这种行为不同于默认行为),所以您需要在jqPlot对象中用值false替换值true。为此,您必须在options对象定义中添加drawGridlines属性,维护结构对象:{property:attribute}。

options = {grid:{drawGridlines: false}};

现在,你有了一个没有网格线的图表(见图 8-3 )。

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

图 8-3。

Hiding the grid lines in a line chart

关于可以设置的属性的完整列表,可以去 jqPlot 官方网站( www.jqplot.com/docs/index/General.html )或者阅读每个发行版中包含的jqPlotOptions.txt文件。

轴上的处理选项

轴的处理与其他普通组件对象略有不同,因为它们有四个不同的子对象,即xaxisyaxisx2axisy2axis。为了说明轴,我们需要一个嵌套更深的例子。假设您想要指定 y 轴上的minmax属性。为此,你需要用清单 8-7 所示的结构指定options对象。

清单 8-7。ch8_02c.html

var options = {

axes:{

yaxis:{

min:70,

max:150

}

}

};

现在,y 轴上的范围在您在options中定义的maxmin属性之间(参见图 8-4 )。

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

图 8-4。

A line chart focused on a specific range on the y axis

为了使事情变得更简单,jqPlot 提供了一个方便的快捷方式,使我们能够一次为所有轴的属性分配相同的值:对象axesDefaults。如果你想给 x 和 y(或者 x2 和 y2)设置相同的值,你只需要为axesDefaults选项对象指定这些属性,赋值一次(见清单 8-8)。

清单 8-8。ch8_02d.html

$(document).ready(function(){

var options = {

axesDefaults:{

min:0,

max:20

}

};

$.jqplot ('myChart', [[1,4,8,13,8,7,12,10,5]], options);

});

插入一系列数据

前面,您已经看到了如何使用标准选项的设置生成简单的折线图(参见“创建图”一节)。在这个例子中,数据数组作为函数$.jqplot()中的第二个参数被直接传递。但是,您也可以在外部将数据数组定义为变量,然后将其作为第二个参数传递。

$(document).ready(function(){

var data = [[100,110,140,130,80,75,120,130,100]];

$.jqplot ('myChart', data);

});

在这里,您可以找到与 y 轴上的值相对应的单个数据系列。但是,正如您将看到的,可以将数据作为(x,y)值对传递,也可以一次传递多个数据系列。这些输入模式各不相同,这取决于您正在设计的图表以及所使用的各种插件的要求。例如,如果你想输入多个数据序列,你需要声明四个不同的数组,如清单 8-9 所示。

清单 8-9。ch8_03a.html

$(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]);

});

jqPlot 能够管理多个系列,而无需在options中指定任何属性。事实上,浏览器会显示一个折线图,有多少条数据系列就有多少条线,每条线都有不同的颜色,如图 8-5 所示。

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

图 8-5。

A multiseries line chart with different colors for each series

正如您所看到的,除了使代码更加可读和整洁之外,外部定义的数据系列还可以帮助将来扩展和操作数据。使用 JavaScript 提供的所有工具,您可以创建、操作、排序、计算和计算各种各样的数据。

甚至对于一系列数据,也可以改变options对象的属性。这些序列按照特定的顺序被插入到一个数组中,该数组作为第二个参数在$.jqplot()函数中传递。这个顺序将反映在jqPlot对象中的series对象的创建中。例如,如果您只希望第二个系列不显示其标记点,则有必要为第一个系列的属性留出空间(不覆盖其属性),然后在第二个空间中将showMarker属性设置为'false'。这样,jqPlot 将只覆盖第二个系列的属性值。为了实现这一点,你必须编写清单 8-10 所示的options对象。

清单 8-10。ch8_03b.html

$(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];

var options = {

series: [ {},

{

showMarker: false

}]

}

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

});

这些设置的结果就是图 8-6 中的四个系列的图表。请注意,从顶部开始的第三个系列没有标记点。

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

图 8-6。

A multiseries chart with a series showing no markers

如果你决定在axesDefaults中设置showMarker属性,而不是在axes对象中,你将一次为所有的序列分配相同的值(见清单 8-11)。

清单 8-11。ch8_03c.html

$(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];

var options = {

seriesDefaults: { showMarker: false }

};

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

});

现在,图表中没有一个系列显示任何标记点(见图 8-7 )。

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

图 8-7。

A multiseries chart with no markers

还有第三种方法将数据作为数组输入。您刚刚看到了这样一种情况,其中为每个系列定义了一个数组,然后传递给$.jqplot()函数,所有这些都聚集在一个数组中:

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], options);

但是,也可以在一个变量中定义所有的序列,我们称之为dataSets:

var dataSets = {

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

};

一旦您声明了dataSets变量,为了访问这些值,您必须指定其中的序列,并以dataSets.作为前缀。因此,当你需要将这四个数列作为jqplot()函数的第二个参数单独传递时,你必须这样做:

$.jqplot ('myChart', [dataSets.data1, dataSets.data2, dataSets.data3, dataSets.data4], options);

虽然,目前,这整个操作可能看起来太费力了,但是稍后您将会看到,在特殊情况下,将所有数据收集到一个数据集中是非常有用的。

渲染器和插件:进一步说明

通常,渲染器是一个对象,它被附加到绘图中的某个对象上以便进行绘制。插件除了添加绘图功能外,还可以执行其他功能,如事件处理;进行计算;以及处理字符串和值的格式,比如日期。因此,可以将渲染器视为绘图插件,但反过来就不一定了。

让我们借助一些例子来更详细地考察这种细微的差别。例如,您已经看到,通过只输入一个数据系列,您可以默认获得一个折线图(参见“创建绘图”一节)。如果要将这个系列渲染成条形图,需要将 barRenderer 插件附加到options中的seriesDefaults对象上。此外,当从折线图切换到条形图时,有必要在 x 轴上创建类别,以便使条形相互之间很好地分开。为此,你需要在options中将CategoryAxisRenderer附加到axes对象上(见清单 8-12)。

清单 8-12。ch8_04a.html

$(document).ready(function(){

var data = [[100, 110, 140, 130, 80, 75, 120, 130, 100]];

var options = {

seriesDefaults: {

renderer: $.jqplot.BarRenderer

},

axes:{

xaxis:{

renderer: $.jqplot.CategoryAxisRenderer

}

}

}

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

});

但是,调用options中的两个渲染器是不够的。您还必须在页面中加载它们,因此您必须包含相应的插件,如清单 8-13 和 8-14 所示(CDN 服务)。

清单 8-13。ch8_04a.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" src="../src/plugins/jqplot.barRenderer.min.js"></script>

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

清单 8-14。ch8_04a.html

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

<script type="text/javascript" src="http://cdn.jsdelivr.net/jqplot/1.0.8/jquery.jqplot.min.js

<link rel="stylesheet" type="text/css" href="http://cdn.jsdelivr.net/jqplot/1.0.8/jquery.jqplot.min.css

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

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

如果在浏览器中重新加载页面,折线图就变成了条形图,如图 8-8 所示。

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

图 8-8。

A bar chart

通过调用这两个渲染器到options,您可以用这些类别渲染器替换对绘图中所有系列有效的默认渲染器。后者又有几个设置为默认值的属性,这些属性也可以修改。这些属性中有许多可能是特定于该特定渲染器的,因此它们将被添加到那些已经在jqplot对象中定义的属性中,以便为绘图引入新的功能。

即使对于这类附加属性,您也可以通过options对象来更改默认值。首先,将所需的渲染器分配给renderer属性。然后,在rendererOptions属性中指定您想要设置的所有属性。所有这些属性都将在您想要操作的组件对象中指定。例如,如果你想让一个给定系列的每一条都有不同的颜色,你需要改变属性varyBarColor,用true替换默认值false(见清单 8-15)。

清单 8-15。ch8_04b.html

var options = {

seriesDefaults: {

renderer: $.jqplot.BarRenderer,

rendererOptions: {

varyBarColor: true

}

},

axes:{

xaxis:{

renderer: $.jqplot.CategoryAxisRenderer

}

}

}

根据您刚才所做的更改,BarRenderer 插件将自动为每个条分配不同的颜色(参见图 8-9 )。

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

图 8-9。

A bar chart with different colors

插件也有特定的属性,可以在options对象中设置。如前所述,并非所有的 jqPlot 插件都是渲染器,那些不是的插件在 jqPlot 发行版中很容易识别,因为它们的文件名中不包含术语渲染器。这些插件执行与图中特定类型的组件不直接相关的特定功能。这些特性总体上增强了 jqPlot 的功能。例如,Highlighter 是一个插件,当鼠标经过数据点时,它会高亮显示这些数据点。正如您将看到的,这个插件中有一系列工具可以处理数据值的格式说明符,并且可以用 HTML 结构显示工具提示内容。其他值得注意的插件包括 Trendline,它自动计算并绘制绘制数据的趋势线;光标,代表光标,显示在图中;和 PointLabels,它在数据点放置标签。

CSS 定制

jqPlot 图表的大部分样式是通过 CSS 完成的。jquery.jqplot.css文件在每个发行版中都可以获得,它是为了获得 jqPlot 图表而包含在您的 web 页面中的三个基本文件之一。

组成图表的所有组件都可以通过 CSS 定制,而不必在options对象中设置它们的任何属性。这是为了保持与网页中所有其他对象的一致性:图表的样式以及其中的所有内容(在画布内部)必须由 CSS 文件管理,就像任何其他 HTML 对象一样。控制 jqPlot 对象样式的 CSS 类的名称以前缀.jqplot-*开始。例如,影响所有轴的样式类是.jqplot-axis

为了说明如何使用 CSS 修改图表的某些元素,让我们看看如何更改图表标题的字体和字体大小。与任何 HTML 元素一样,您只需调用jqPlot元素的 CSS 选择器并修改属性。因此,在这种情况下,您在清单 8-16 中添加 CSS 样式设置。

清单 8-16。ch8_02e.html

<style>

.jqplot-title {

font:italic bold 22px arial,sans-serif;

}

</style>

有了这个新的 CSS 语句,你就改变了标题的风格,如图 8-10 所示。

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

图 8-10。

Two different CSS styles applied to the title

模块思维

当事情变得越来越复杂,要添加到网站的代码变得很多时,最好从模块的角度来考虑。除了提供更好的可视性和易于维护之外,创建单独的模块还可以促进您刚刚创建的内容的可重用性。让我们分析一下清单 8-17 中你的网页的现状。

清单 8-17。ch8_05a.html

<HTML>

<HEAD>

<TITLE>My first chart</TITLE>

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

<style>

.jqplot-title {

font: italic bold 22px arial,sans-serif;

}

</style>

<script class="code" type="text/javascript">

$(document).ready(function(){

var data = [[100, 110, 140, 130, 80, 75, 120, 130, 100]];

$(document).ready(function(){

$.jqplot ('myChart', data,

{

title: 'My first jqPlot chart'

});

});

});

</script>

</HEAD>

<BODY>

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

</BODY>

</HTML>

如果您在浏览器中加载该网页,您将获得图 8-11 中的折线图。

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

图 8-11。

A simple line chart

显而易见,控制页面样式的部分包含在标签对

九、jqPlot 折线图

Abstract

在前一章中,您观察到了 jqPlot 的最基本用法,其中一系列数据用来绘制一条线,不需要任何附加选项。您已经看到,为了创建最基本的图表类型,即折线图,您不需要包含插件。

在前一章中,您观察到了 jqPlot 的最基本用法,其中一系列数据用来绘制一条线,不需要任何附加选项。您已经看到,为了创建最基本的图表类型,即折线图,您不需要包含插件。

在本章中,您将通过探索各种插件及其功能,开始更详细地研究 jqPlot 库提供的可能性。首先,因为折线图是在笛卡尔轴上表示的,所以将向您介绍使用成对的值(x,y)作为输入数据。然后,您将继续学习轴以及如何使用适当的插件创建它们。您还将详细分析如何将连接到轴的各种元素实现为记号、轴标签和网格。对数标度的讨论如下。

接下来,您将学习如何通过同时处理多个系列的数据来实现多系列折线图。您将发现如何通过设置线条和标记来修改图案、形状甚至颜色。此外,您将看到如何通过调整浏览器绘制图表的速度来创建动画。

此外,您将研究 jqPlot 库允许您操作不同格式的日期和时间值的方式。您还将看到如何使用 HTML 格式定制一些元素,以及突出显示数据点。在本章的最后一部分,你将处理更复杂的情况,比如生成趋势线和使用波段图。

使用(x,y)对作为输入数据

到目前为止,为了简单起见,输入数据是以一个 y 值数组的形式传入的(见清单 9-1)。如果 jqPlot 只找到 y 值,则 x 值按照它们在数组中的顺序被指定为 1、2、3 等等。

清单 9-1。ch9_01.html

$(document).ready(function(){

var plot1 = $.jqplot ('myChart', [[100,110,140,130,80,75,120,130,100]]);

});

在图 9-1 中,你可以沿着 x 轴看到一系列整数,它们是作为数据传递的数组的索引。

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

图 9-1。

The x axis reports the indexes of the values inserted

当您使用线性图时,最好使用具有成对数值(x,y)的数组,因为这可以避免许多复杂情况,例如需要以特定顺序输入数据,而这并不总是可能或正确的。实际上,使用成对的值,数据不应该按照 x 值递增的顺序排列;jqPlot 会帮你做到。此外,x 的值不需要等距,而是可以遵循任何分布。在清单 9-2 中,插入了成对的值(x,y ),其中 x 值既没有排序也没有均匀分布。

清单 9-2。ch9_02.html

$(document).ready(function(){

var data = [[[10,100], [80,130], [65,75], [40,130],

[60,80], [30,140], [70,120], [20,110], [95,100]]];

$.jqplot ('myChart', data);

});

在图 9-2 中,你可以看到 jqPlot 是如何对图表中的所有点进行排序的,不管它们输入的顺序如何,也不管它们是否沿 x 轴均匀分布。

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

图 9-2。

A simple line chart with nonuniformly distributed points on the x axis

开发折线图的第一步:轴

在详细研究折线图的更复杂的方面之前,让我们先来看看这种图表的基础:轴。如果您想开发一个实现完美数据可视化的图表,对坐标轴的正确管理是至关重要的。为此,您需要很好地理解 jqPlot 库通过使用options对象中的特定属性提供的动作模式。

添加标题和轴标签

开发图表时,第一步是使用 CanvasAxisLabelRenderer 插件添加标题并管理轴标签。

但是,为了正常工作,这个插件需要另一个插件,一个提供编写功能的插件:CanvasTextRenderer。使用这个插件,您可以直接在画布元素上呈现标签文本。这允许您像对待任何其他图形元素一样对待文本,使您能够随心所欲地旋转文本。默认情况下,y 轴上的轴标签现在旋转了 90 度,如图 9-3 所示。

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

图 9-3。

Without including the CanvasAxisLabelRenderer plug-in , the y axis label is horizontal. When the plug-in is included, the y axis label is rotated vertically

要集成这一新功能,您需要将这两个插件添加到基本插件集中:

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

</script>

<script 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.canvasTextRenderer.min.js></剧本>

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

创建了options变量之后,你必须在里面指定一些属性,如清单 9-3 所示。您已经看到了如何通过给title对象分配一个字符串来添加标题。然后,必须显式调用canvasAxisLabelRenderer对象来激活它的功能,通过在axesDefaults对象中这样做,它将对所有轴有效。要分配 x 轴和 y 轴标签中的文本,必须设置axes对象的xaxisyaxis子对象中的label属性。它的树形结构将允许你在每个单独的轴上进行不同的改变。

清单 9-3。ch9_03a.html

$(document).ready(function(){

var data = [[100, 110, 140, 130, 80, 75, 120, 130, 100]] ;

var options = {

title: 'My Line Chart',

axesDefaults: {

labelRenderer: $.jqplot.CanvasAxisLabelRenderer

},

axes: {

xaxis: {

label: "X Axis"

},

yaxis: {

label: "Y Axis"

}

}

};

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

});

图 9-4 展示了清单代码生成的图表。

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

图 9-4。

A line chart with the y axis label vertically oriented

轴属性

与轴标签一样,有几个属性可以在axes对象中指定。例如,查看图表(见图 9.4),您可以看到直线从 x 值 1 开始,而 x 轴从值 0 开始,因此留下了一个空白。另一个空格出现在 x 范围的末端(9 到 10 之间)。如果您想要作用于这些距离(在轴的界限和数据集的端点之间),您必须使用pad属性。您可以应用填充来扩展数据边界上下的范围。数据范围乘以该因子,以确定最小和最大轴边界。值 0 将被解释为没有填充,并且pad将被设置为 1。因此,通过将pad属性添加到xaxis对象并将pad设置为 1(参见清单 9-4),您得到了图 9-5 中的图表。

清单 9-4。ch9_03b.html

xaxis: {

label: "X Axis",

pad: 1

},

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

图 9-5。

The same line chart as in Figure 9-4, with pad set to 1 on the x axis

现在,x 轴从值 1 开始,以 9 结束,代表数据系列的线也是如此。为了更好地理解填充的概念,你现在将设置pad属性为 2(见清单 9-5)。这意味着您想要将当前范围(10)扩大两倍。结果,你会得到一个 x 轴从-4 到 14 的图表,如图 9-6 所示。这是因为 jqPlot 倾向于以对称的方式保存数据,将其显示在中间。

清单 9-5。ch9_03c.html

xaxis: {

label: "X Axis",

pad: 2

},

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

图 9-6。

The same line chart, with pad set to 2 on the x axis

另一种控制数据显示范围的方法是使用minmax属性(见清单 9-6)。

清单 9-6。ch9_03d.html

xaxis: {

label: "X Axis",

min: 1,

max: 9

},

图 9-7 显示了具有新范围的 x 轴。

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

图 9-7。

The same line chart, with defined max and min on the x axis

其他有用的属性是那些控制细分(分割轴)及其底层数字术语的属性:ticks属性。由于它们的使用不仅限于axes对象下的简单选项——它们本身也是一个对象,并且需要一个渲染器插件才能工作——它们的处理值得用一个单独的章节来介绍。

坐标轴刻度

记号是显示图中记号或网格线值的组件。可以在options中的axes对象内指定图中刻度的行为,而且,作为一个对象本身,刻度有几个属性可以在tickOption属性内设置。例如,您可能需要为每个轴设置特定数量的网格线。这可以用不同的方法来完成。最简单的是直接指定numberTicks属性(见清单 9-7)。如果将其值设置为 5,您将在 x 轴上得到五个刻度:0、3、6、9 和 12(参见图 9-8 )。

清单 9-7。ch9_03e.html

xaxis: {

label: "X Axis",

numberTicks: 5

},

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

图 9-8。

A line chart with a prefixed number of ticks on the x axis

这也适用于 y 轴。在这种情况下,您需要在yaxis对象中设置相同的属性。从图中可以看出,x 轴的间隔是均匀的,因此刻度是等距的。另一种方法是直接定义你想要在图表上显示的刻度,如清单 9-8 所示。

清单 9-8。ch9_03f.html

xaxis: {

label: "X Axis",

ticks: [0,3,6,9,12]

},

这产生了相同的图表(见图 9-9 )。

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

图 9-9。

A line chart with directly defined ticks on the x axis

但是,当您希望刻度沿轴不均匀分布时,通常最好使用这种方法,如清单 9-9 所示。网格线也将遵循这种不均匀性,因为它将与每个刻度对应绘制(见图 9-10 )。

清单 9-9。ch9_03g.html

xaxis: {

label: "X Axis",

ticks: [1,2,3,7,9]

},

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

图 9-10。

A line chart with nonuniform, prefixed ticks on the x axis

刻度在图表中如此重要,以至于它们有一个专门针对它们的插件:CanvasAxisTickRenderer。

如果希望创建一个没有网格线的图表,同时保持刻度上的值,可以将showGridLine属性设置为'false'。但是,在此之前,您需要在 web 页面中包含插件:

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

<script type="text/javascript"

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

<script type="text/javascript"

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

<script type="text/javascript"

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

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

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

/jqplot.canvasTextRenderer.min.js"></script>

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

/jqplot.canvasAxisLabelRenderer.min.js"></script>

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

/jqplot.canvasAxisTickRenderer.min.js"></script>

然后你必须在axesDefaults对象内进行设置,因为你想隐藏两个轴的网格线。记得调用刚刚包含在tickRenderer属性中的插件(见清单 9-10)。此外,您一定不要忘记删除在xaxis对象中定义的ticks属性。

清单 9-10。ch9_04a.html

axesDefaults: {

labelRenderer: $.jqplot.CanvasAxisLabelRenderer,

tickRenderer: $.jqplot.AxisTickRenderer,

tickOptions: {

showGridline: false

}

},

axes: {

xaxis: {

label: "X Axis"  //remove the comma here

},

如图 9-11 所示,你得到一个没有网格的图表。

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

图 9-11。

A line chart without grid lines

有时,你只需要隐藏一个轴的网格线,例如 x 轴(见图 9-12 )。在这种情况下,您必须只在xaxis对象内部调用渲染器。在清单 9-11 中,你可以看到必须从axesDefaults中删除然后写入xaxis对象的代码行。

清单 9-11。ch9_04b.html

axesDefaults: {

labelRenderer: $.jqplot.CanvasAxisLabelRenderer

//delete all this lines

//tickRenderer: $.jqplot.AxisTickRenderer,

//tickOptions: {

//showGridline: false

//}

},

axes: {

xaxis: {

label: "X Axis",

tickRenderer: $.jqplot.AxisTickRenderer,

tickOptions: {

showGridline: false

}

},

...

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

图 9-12。

A line chart with only horizontal grid lines

您可能希望添加的另一个功能是允许您将数值的格式作为字符串来处理。最常见的情况是,当您想要在 y 轴上显示百分比值时,这可能是有用的。要做到这一点,你需要在数值后面加上字符'%',如清单 9-12 所示。

清单 9-12。ch9_04c.html

yaxis: {

label: "Y Axis",

tickRenderer: $.jqplot.AxisTickRenderer,

tickOptions: {

formatString:'%d%'

}

}

如图 9-13 所示,图表现在在 y 轴上报告百分比值。

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

图 9-13。

A line chart reporting percentages on the y axis

稍后,您将看到这种字符串格式被证明是一种非常强大的工具的其他情况(参见“处理日期值”一节)。

使用对数标度

根据要在图表中表示的数据趋势,有时有必要在一个或两个轴上使用对数刻度。jqPlot 支持对数标度,包括 web 页面中的 LogAxisRenderer 插件。

<script type="text/javascript"

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

LOG SCALE

对数标度使用与数量级(通常为 10)相对应的间隔,而不是标准的线性标度。这允许您在轴上表示大范围的值(v)。A978-1-4302-6290-9_9_Fig1a_HTML.jpg A978-1-4302-6290-9_9_Fig2a_HTML.jpg

对数是指数的另一种写法,你可以用它来分隔指数(x)并把它放在一个轴上。A978-1-4302-6290-9_9_Fig3a_HTML.jpg A978-1-4302-6290-9_9_Fig4a_HTML.jpg

例如,对数标度上一个点的增加对应于该值的 10 倍增加。同样,增加 2 个点相当于该值增加 100 倍。等等。

在想要以对数刻度表示数据的轴上,只需添加带有插件引用的renderer属性。在这种情况下,您需要创建一个近似遵循指数趋势的数据数组。所以,你在清单 9-13 中使用了[x,y]对的数组。

清单 9-13。ch9_11.html

var data = [[0,1.2],[10,2.4],[20,5.6],[30,12],[40,23],

[50,60],[60,120],[70,270],[80,800]];

接下来,你把 y 轴放在对数刻度上,如清单 9-14 所示。

清单 9-14。ch9_11.html

$.jqplot ('myChart', [data],{

axes:{

xaxis:{},

yaxis:{ renderer: $.jqplot.LogAxisRenderer }

}

});

在图 9-14 中,您可以看到数据是如何在半对数标度(一个轴上的对数标度)中呈现出近似于直线的形状。

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

图 9-14。

A line chart on a semilog scale on the y axis

多系列折线图

既然已经很好地指定了表示折线图的坐标轴,现在是处理多系列折线图的时候了。通常,您需要在同一个图表中显示多个数据系列。事实上,图表的目的通常就是比较不同的数据序列。

jqPlot 库为我们提供了管理多系列图表所需的工具。通过对线条和标记的图案、形状和颜色进行操作,可以引入有助于表示不同数据系列的图形效果。

多系列数据

到目前为止,您只处理了一组数据。但是,有时您希望一次表示多个数据集。在第一章中,你看到了在 jqPlot 中,多个系列的处理方式与单个系列相同。每个系列必须首先通过将它赋给一个变量来单独定义,然后在一个数组中与其他系列组合。然后这个数组作为第二个参数传递给jqPlot()函数(见清单 9-15)。

清单 9-15。ch9_05a.html

$(document).ready(function(){

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

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

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

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

var options = {

title:'Multiple Data Arrays'

};

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

});

图 9-15 显示了清单 9-15 中的多序列图表。

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

图 9-15。

A multiseries line chart

系统自动为每个系列赋予不同的颜色。这个颜色序列在 jqPlot 中被定义为缺省值。以下是 jqPlot 将按顺序分配给系列的颜色:

seriesColors: [  "#4bb2c5", "#c5b47f", "#EAA228", "#579575",

"#839557", "#958c12", "#953579", "#4b5de4",

"#d8b83f", "#ff5800", "#0085cc"]

这些值代表'#rrggbb',其中 rr、gg 和 bb 是红色、绿色和蓝色的十六进制值。浏览器结合这些值来生成系列所需的所有颜色。

当有超过 11 个系列时,jqPlot 将从头开始再次启动该系列。如果您不希望这样,或者只是需要做些不同的事情,您可以在seriesColors属性中定义一个不同颜色序列的数组,如清单 9-16 中给出的序列。图 9-16 显示了一种灰色的变化,但是运行这个例子,自己看看有什么不同(颜色从蓝色到紫色)。

Note

要检查颜色代码,我建议访问网站 HTML 颜色代码( http://html-color-codes.info )。

清单 9-16。ch9_05b.html

var options = {

seriesColors: ["#105567","#805567","#bb5567","#ff5567"],

title:'Multiple Data Arrays'

};

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

图 9-16。

A multiseries line chart with a customized color set

你也可以属性一个特定的颜色,使用两个函数:rgba( r , g , b , a )rgb( r , g , b )。将这些函数直接插入到分配给seriesColors属性的数组的每个值中,如清单 9-17 所示。

清单 9-17。ch9_05c.html

seriesColors: ["rgba(16,85,103,0.2)", "rgba(128,85,103,0.6)",

"rgb(187,85,103)", "rgb(250,85,103)"],

鉴于您已经通过获得给定颜色所需的红光、绿光和蓝光的组合来指定颜色,使用rgba()函数,引入了一个新变量a。这个a(代表“alpha”)代表一种颜色的不透明度。如图 9-17 所示,定义低 alpha 值可以让你看到有色物体背后的东西。

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

图 9-17。

A multiseries line chart with different levels of transparency

平滑线图

除了选择是否表示点标记和连接它们的直线,通常你会决定你想要得到一个平滑的曲线进展,如图 9-18 所示。这可以简单地通过使用smooth属性并将其设置为'true'来完成(见清单 9-18)。

清单 9-18。ch9_06.html

$(document).ready(function(){

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

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

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

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

var options = {

title:'Multiple Data Arrays',

seriesDefaults: {

rendererOptions: {

smooth: true

}

}

};

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

});

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

图 9-18。

A multiseries line chart with smoothed lines

线条和标记样式

设计折线图时,您需要考虑的另一个关键方面是线条和标记的显示方式。您可以使用线条、标记序列或两者来表示图表。默认情况下,jqPlot 显示每个系列,每个点对应于[x,y]对的点标记和一条按顺序连接它们的线。

所有这些都可以使用 series 对象的两个关键属性来控制:linePattern 和 lineWidth 在添加markerOptions属性的同时,还可以作用于影响标记组件的另外两个属性:style 和 size。清单 9-19 是这些设置的一个例子。

清单 9-19。ch9_07a.html

$(document).ready(function(){

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

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

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

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

var options = {

title: 'Multiple Data Arrays',

series:[{

linePattern: 'dashed',

lineWidth:2,

markerOptions: { style: 'diamond' }

},

{

showLine:false,

markerOptions: { size: 7, style: 'x' }

},

{

markerOptions: { style: 'circle' }

},

{

lineWidth:5,

linePattern: 'dotted',

markerOptions: { style: 'filledSquare', size: 10 }

}]

}

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

});

图 9-19 展示了清单 9-19 中设置的结果。

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

图 9-19。

In a line chart it is possible to set different markers and patterns

图表上的线条可以用linePattern属性绘制为实线、虚线或点线。默认情况下,绘制的每条线都是实心的,所以如果您想要一条线有不同的样式,有必要在options中指定。你可以在清单 9-19 中看到,可以将linePattern属性设置为'dotted''dashed',以便分别获得虚线或虚线。在清单 9-20 中,你可以看到也可以获得一个定制的线型,将格式定义为一个数组([破折号长度,间隙长度等等])。当分配给linePattern属性的数组有偶数个元素时,线条看起来最好,这样线条以破折号开始,以空格结束。linePattern属性也可以创建一个定制的模式,使用破折号(-)和点(.)字符的简写字符串符号。清单 9-20 提供了一些例子。

清单 9-20。ch9_07b.html

var options = {

title: 'Multiple Data Arrays',

seriesDefaults: {

showMarker: false

},

series: [{ linePattern: 'dashed'},

{ linePattern: 'dotted'},

{ linePattern: [4, 3, 1, 3, 1, 3]},

{ linePattern: '-.'}]

};

图 9-20 显示了清单 9-20 中使用的定制线条模式的例子。

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

图 9-20。

A multiseries line chart with different patterns

动画图表

当您在浏览器中加载网页时,您会注意到图表几乎是即时绘制的。你可以放慢绘图速度,根据你的喜好进行调整;当绘制元素时,较慢的速度会给图表带来浮动效果(见清单 9-21)。

清单 9-21。ch9_23.html

var options = {

title: 'Multiple Data Arrays',

seriesDefaults: {

showMarker: false,

rendererOptions: {

smooth: true,

animation: { show: true  }

}

}

};

图 9-21 显示了绘制图表的顺序,给人一种动画的感觉。

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

图 9-21。

An animated multiseries line chart

多个 y 轴

jqPlot 支持与同一个 x 轴相关的多个 y 轴。当您希望在单个图表中显示分布在不同 y 刻度上但具有相同 x 值的不同系列时,这很有用。在这种情况下,明智的做法是用相应系列的颜色来设置 y 轴,以便您可以确定任何给定点的正确 y 值。作为输入数据,让我们创建三个数据数组,它们包含相同的 x 值,但 y 值分布在不同的范围内,如清单 9-22 所示。使用相同的 x 值不是强制性的,但这样做是明智的。

清单 9-22。ch9_12.html

var data1 = [[10, 200], [20, 230], [30, 214], [40, 212], [50, 225], [60, 234]];

var data2 = [[10, 455], [20, 470], [30, 465], [40, 432], [50, 455], [60, 464]];

var data3 = [[10, 40], [20, 60], [30, 54], [40, 52], [50, 65], [60, 54]];

为每个 y 轴指定正确的数值范围是非常重要的,以便能够容易地比较不同系列的数值(见清单 9-23)。在series对象中,您需要明确指定三个值,每个值将一个序列分配给不同的 y 轴。如果您想保留特定系列的默认设置,即沿默认 y 轴的表示,您仍必须在对应于该系列的位置分配一个空对象{}。事实上,在这个例子中,series数组的第一个元素只是一个空对象{}.

此外,您需要将axesDefaults对象的useSeriesColor属性设置为'true'。这样,jqPlot 会将系列的颜色分配给相应的 y 轴。因此,通过使用三种默认颜色,第一个系列将为浅蓝色,第二个系列为橙色,第三个系列为灰棕色。

清单 9-23。ch9_12.html

var options = {

series:[

{},

{yaxis: 'y2axis'},

{yaxis: 'y3axis'}

],

axesDefaults:{useSeriesColor: true},

axes:{

xaxis: {min: 0, max: 70},

yaxis: {min: 190, max: 240},

y2axis: {min: 430, max: 480},

y3axis: {min: 35, max: 80}

}

};

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

图 9-22 显示了三个系列,每个系列都以其 y 轴值表示。这里的轴以不同的灰度阴影显示,但它们实际上采用了与相关系列相对应的颜色。

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

图 9-22。

A multiseries line chart with multiple y axes

JavaScript 数据

如前所述,最好在jqPlot函数之外单独定义数据数组。您已经看到了如何创建一个包含 y 值或[x,y]对数值的数组。然而,因为 jqPlot 属于 JavaScript 领域,所以有另一种方法经常被证明是非常有用的:通过 JavaScript 方法生成数据序列。

使用数学函数生成数据

jqPlot 库是基于 JavaScript 的,并且和所有编程语言一样,它允许您实现生成值序列以用作输入数据的函数。例如,清单 9-24 采用了三个最常用和最著名的数学函数(正弦、余弦、幂)并通过它们创建了一个数据数组。

清单 9-24。ch9_08a.html

$(document).ready(function(){

var options = {

title:'Math function Arrays'

};

varcosPoints = [];

for (vari=0; i< 2 * Math.PI; i += 0.1){

cosPoints.push([i, Math.cos(i)]);

}

varsinPoints = [];

for (vari=0; i< 2 * Math.PI; i += 0.1){

sinPoints.push([i, 2 * Math.sin(i-.8)]);

}

varpowPoints = [];

for (vari=0; i< 2 * Math.PI; i += 0.1) {

powPoints.push([i, 2.5 + Math.pow(i/4, 2)]);

}

$.jqplot ('myChart', [cosPoints, sinPoints, powPoints], options);

});

图 9-23 说明了列表中三个函数生成的点如何在折线图上形成三个数学函数的特征趋势。

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

图 9-23。

A line chart reporting three different series of data generated from mathematical functions

因为这是一个具有高密度点的函数,并且因为这里的目标是突出趋势,所以最好不要显示标记点(见图 9-24 )。最好在options,中启用平滑,如清单 9-25 所示。

清单 9-25。ch9_08b.html

var options = {

title: 'Math function Arrays',

seriesDefaults: {

rendererOptions: {

smooth: true

},

markerOptions: { show: false }

}

};

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

图 9-24。

The same line chart, but rendered more legibly

生成随机数据

您已经看到了如何使用数学函数生成输入数据。同样,有时需要生成随机数据。例如,假设您刚刚完成 jqPlot 图表的编写,并且想要尝试输入虚拟数据。为此,使用随机生成的数据是最好的。清单 9-26 中的函数产生随机数据,每个点都是根据前一个点的值产生的。在每一步中,新值由一个随机数决定,该随机数与前一个数相加或相减。这会产生一系列连续的数据,从作为参数传递给函数的值开始。

清单 9-26。ch9_09.html

function generateRandomData(npts, start, delta) {

var data = [];

if (delta == null) {

delta = start;

start = (Math.random() - 0.5) * 2 * delta;

}

for (j=0; j<npts; j++) {

data.push([j, start]);

start += (Math.random() - 0.5) * 2 * delta;

}

return data;

}

您使用了三个参数:npts是要生成的点数,start是初始值,delta是每步随机加减的最大值。该函数返回一个数组,该数组将作为输入数据传递给图表。您可以从外部定义它:

var data = generateRandomData(30, 100, 1);

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

或者,你可以直接传递:

$.jqplot ('myChart', [makeContinuousData(30, 100, 1)]);

结果你得到了一个类似图 9-25 的图表(每次都会不一样)。

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

图 9-25。

A line chart representing a random series of data

处理日期值

日期类型是一种常用的值,尤其是在其他图表(如条形图)中。这些专门化的值不是那么容易处理的,jqPlot 有一个针对它们的插件:DateAxisRenderer。这个插件扩展了 JavaScript 的本机日期处理功能,允许您以任何明确的形式表示日期值,而不仅仅是以毫秒为单位。

DateAxisRenderer 插件

日期可以用多种方式表示,其格式因国家和用途而异。日期由日、月和年指示器组成。这些可以不同地排序,并且具有一位、两位或四位数字;或者,您甚至可能希望只使用一个或两个指示器(例如,月、年)。此外,各种字符充当分隔符。让我们以 04/07/2012 为例:“4”代表第四个月(四月),“7”是该月的第七天,“2012”是年份。这样的日期可以用多种方式显示:'07/04/2012''07/04/12''04/07/12''7-Apr-12''7-Apr''Apr-12''7 April''2012'等等。

日期值的标准格式如下:

'YYYY-MM-DD HH:MM<PM or AM>'

这个字符串包含了所有必要的信息——可能有点多。事实上,您通常只需要日期信息的一部分:有时,您可能只需要报告日和月,或者,如果您引用时间,您可能只需要处理小时和分钟,等等。

一旦包含了 DateAxisRenderer 插件,jqPlot 几乎可以接受任何可识别的值。在值被内部解析后,它将被呈现在调用插件的轴上,以tickOptions.formatString中指定的格式表示。

表 9-1 显示了可接受的格式代码。

表 9-1。

Date and Time Formats Accepted by jqPlot

| 密码 | 结果 | 描述 | | --- | --- | --- | | 年 |   |   | | `%Y` | Two thousand and eight | 四位数年份 | | `%y` | 08 | 两位数的年份 | | 月份 |   |   | | `%m` | 09 | 两位数的月份 | | `%#m` | nine | 一位数或两位数的月份 | | `%B` | 九月 | 完整的月份名称 | | `%b` | 九月 | 缩写月份名 | | 天 |   |   | | `%d` | 05 | 两位数的月份日期 | | `%#d` | five | 一位数或两位数的月份中的某一天 | | `%e` | five | 一位数或两位数的月份中的某一天 | | `%A` | 在星期日 | 星期几的全名 | | `%a` | 太阳 | 星期几的缩写名称 | | `%w` | Zero | 一周中的第几天(0 =星期日,6 =星期六) | | `%o` | 泰国(Thailand) | 一个月中某一天之后的序数后缀字符串 | | 小时 |   |   | | `%H` | Twenty-three | 24 小时制的小时数(两位数) | | `%#H` | three | 24 小时整数格式的小时数(一位数或两位数) | | `%I` | Eleven | 12 小时制的小时数(两位数) | | `%#I` | three | 12 小时整数格式的小时数(一位数或两位数) | | `%p` | 下午 | 上午或下午 | | 分钟 |   |   | | %M | 09 | 分钟(两位数) | | %#M | nine | 分钟(一位数或两位数) | | 秒 |   |   | | `%S` | 02 | 秒(两位数) | | `%#S` | Two | 秒(一位数或两位数) | | `%s` | 1206567625723 | Unix 时间戳(1970 年 1 月 1 日 00:00:00 之后的秒数) | | 毫秒 |   |   | | `%N` | 008 | 毫秒(三位数) | | `%#N` | eight | 毫秒(一到三位数) | | 时区 |   |   | | `%O` | Three hundred and sixty | 当地时间和格林威治标准时间(GMT)之间的时差(分钟) | | `%Z` | 山地标准时间(MST) | 浏览器报告的时区名称 | | `%G` | –06:00 | GMT 之间的小时和分钟 | | 快捷指令 |   |   | | `%F` | 2008-03-26 | %Y-%m-%d | | `%T` | 05:06:30 | %H:%M:%S | | `%X` | 05:06:30 | %H:%M:%S | | `%x` | 03/26/08 | %m/%d/%y | | `%D` | 03/26/08 | %m/%d/%y | | `%#c` | 2008 年 3 月 26 日星期三下午 3:31 | %a %b %e %H:%M:%S %Y | | `%v` | 2008 年 9 月 3 日 | %e-%b-%Y | | `%R` | twenty nine to four p.m. | %H:%M | | `%r` | 下午 3 时 31 分 | %I:%M:%S %p | | 特性 |   |   | | `%n` | \n | 换行 | | `%t` | \t | 标签 | | `%%` | % | 百分比符号 |

为了更清楚地了解 jqPlot 如何处理日期值,让我们看一系列说明各种格式的例子。然而,不管是哪种格式,您都必须在 web 页面的<head>部分包含 DateAxisRenderer 插件。

<script type="text/javascript"

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

处理不同格式的日期值

第一个例子处理一段时间内的汇率,每天的点值。为此,输入数据数组内部应该有一系列[x,y]值,其中 x 是一个日期值。x 值的序列不符合时间顺序;jqPlot 将沿着 x 轴对这些点进行排序。在清单 9-27 中,你使用了一系列的 x 输入值,前五个值有不同的格式。

清单 9-27。ch9_13a.html

var line1 = [['14-Oct-2012', 1300.41], ['2012-10-15', 1310.50],

['2012/10/16', 1322.88], ['17 Oct 2012', 1312.41],

['10/18/2012', 1308.16], ['19-Oct-2012', 1310.71],

['20-Oct-2012', 1305.01],['21-Oct-2012', 1300.85],

['22-Oct-2012', 1290.67]];

接下来,您必须调用options中的xaxis对象内部的渲染器来激活它。您希望表示一个月中跟随汇率值趋势的日子,因此您将设置不包括年份的输出格式,年份保持不变。此外,在开始时,您希望以数字形式显示一个月中的某一天,然后用前三个字符书写月份,中间用空格隔开。简单地说,在清单 9-28 中,格式将是'%d %b',其中%d代表数字形式的日,%b,代表月份的前三个字符。y 值是美元,所以需要添加美元符号($)作为 y 轴刻度的前缀。为此,您还必须对 y 刻度使用formatString属性。

清单 9-28。ch9_13a.html

var options = {

title: 'Handling Date Values',

axes:{

xaxis:{

renderer: $.jqplot.DateAxisRenderer,

tickOptions:{

formatString:'%d %b'

}

},

yaxis:{

tickOptions:{

formatString:'$%d'

}

}

}

};

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

图 9-26 显示美元值,前缀$,在 y 轴上,日和月在 x 轴上。这只是您可以设置来表示刻度值的几种格式之一。

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

图 9-26。

A line chart with date values on the x axis

处理时间值

假设您想要绘制一个图表来表示参观博物馆的次数。可以在输入数据中明确显示时间(小时、分钟、秒)。这允许你以与前一个例子(见清单 9-27)相同的方式处理这些时间值,例如,通过创建一个包含某一天收集的数据的图表。这里也可以用前面讨论过的任何格式设置日期。您可以用各种方式表示时间:12 小时格式,带 am 或 pm 后缀,或者直接用 24 小时格式,包括或忽略秒和分钟。清单 9-29 展示了一个以 2 小时为间隔的时间值序列。

清单 9-29。ch9_13b.html

var line1 = [['2012-10-14 08:00AM', 30],['2012-10-14 10:00AM', 60],

['2012-10-14 00:00PM', 120], ['2012-10-14 02:00PM', 60],

['2012-10-14 04:00PM', 100], ['2012-10-14 06:00PM', 40]];

关于输出格式,您必须记住还要管理时间格式;因为您只对一天中的几个小时感兴趣,所以您将'%R'设置为'formatString'(见清单 9-30)。

清单 9-30。ch9_13b.html

var options = {

title: 'Museum Visitors',

axes:{

xaxis:{

label: 'time',

renderer:$.jqplot.DateAxisRenderer,

tickOptions:{

formatString: '%R'

}

},

yaxis:{

label: 'visitors'

}

}

};

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

浏览器将显示如图 9-27 所示的图表。

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

图 9-27。

A bar chart with time values on the x axis

突出

可以添加到图表中的一个引人注目的效果是突出显示(即,让您的绘图对鼠标悬停做出反应。例如,荧光笔插件会高亮鼠标附近的数据点,具有很好的动态效果。这可以通过显示带有数据点值的工具提示来增强。

光标荧光笔

下面的例子将帮助你熟悉高亮显示。该功能非常重要,当鼠标悬停在图表中的特定元素上时会激活一个事件。通常,这些是表示数据的元素,例如,在折线图中,用一个点(或者,更准确地说,用标记;您会发现这也适用于其他类型的图表:条形图中的条形、饼图中的切片等等。

默认情况下,触发的事件只是数据的一个突出显示,由显示其(x,y)值的工具提示表示。

要将这些功能添加到图表中,您必须包括一组插件:

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

</script>

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

</script>

在清单 9-31 中,作为输入数据,你使用了一系列的[x,y]对,其中日期值在 x 轴上,数值在 y 轴上。

清单 9-31。ch9_14a.html

var line1 = [['14-Oct-12', 1300.41], ['15-Oct-12', 1310.50],['16-Oct-12', 1322.88],

['17-Oct-12', 1312.41],['18-Oct-12', 1308.16],['19-Oct-12', 1310.71],

['20-Oct-12', 1305.01],['21-Oct-12', 1300.85],['22-Oct-12', 1290.67]];

正如您已经看到的,为了处理日期值,您需要包括 DateAxisRenderer 插件。

<script type="text/javascript"

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

在清单 9-32 中,你可以看到options对象,包含两个新对象:highlightercursor

清单 9-32。ch9_14a.html

var options = {

title: 'Data Point Highlighting',

axes:{

xaxis:{

renderer: $.jqplot.DateAxisRenderer,

tickOptions:{

formatString: '%b&nbsp;%#d'

}

},

yaxis:{

tickOptions:{

formatString: '$%d'

}

}

},

highlighter:{

show: true,

sizeAdjust: 7.5

},

cursor:{

show: false

}

};

在图 9-28 中,当光标移动到图表上的数据点上时,会出现一个工具提示。默认情况下,此工具提示使用轴格式化程序报告 x 和 y 值,用逗号分隔,但这可以用不同的格式字符串自定义。

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

图 9-28。

Data point highlighting on a line chart

在清单 9-32 中,你会注意到光标已经被禁用,通过将它的show属性设置为'false'(默认情况下是启用的)。启用它,如清单 9-33 所示,你会看到鼠标光标在进入图形区域时发生变化,并在右下角显示一个可选的工具提示,报告鼠标位置。工具提示可以位于固定位置,也可以跟随鼠标移动。默认设置为'crosshair'的指针样式也可以自定义。

清单 9-33。ch9_14b.html

...

highlighter: {

show: true,

sizeAdjust : 7.5

}

cursor: {

show: true,

tooltipLocation:'ne'

}

});

图 9-29 显示了报告光标坐标的工具提示。请注意,光标由图表中间的黑色十字表示。

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

图 9-29。

A line chart showing the cursor coordinates

用 HTML 格式突出显示

您可以使用 HTML 标签作为格式来更改工具提示的内容。这使得定制的可能性几乎是无限的。事实上,你可以把工具提示想象成一个小小的网页,可以在其中添加任何类型的元素,比如一张图片或者一个锚链接(更多细节,参见第一章 0)。例如,您可以使用清单 9-34 所示的设置,将 HTML 格式字符串分配给formatString属性。

清单 9-34。ch9_14c.html

highlighter: {

show: true,

sizeAdjust: 7.5,

showMarker: false,

tooltipAxes: 'xy',

yvalues: 4,

formatString:'<table class="jqplot-highlighter"> \

<tr><td>date:</td><td>%s</td></tr> \

<tr><td>value:</td><td>%s</td></tr></table>'

},

因此,带有内容的工具提示将表现得像一个小的 HTML 页面,如图 9-30 所示。

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

图 9-30。

A line chart with an HTML tool tip

与图表交互:限制线条和缩放

一旦你有了一个包含图形和元素的折线图,下一步就是引入交互元素。例如,用户可能需要使用阈值来查看这些值之外的数据。用户可能还需要改变该阈值,以确定哪些数据在阈值之内,哪些数据在阈值之外。通常,会表示大量数据。在这种情况下,用户可能只需要分析一个细节。

jqPlot 库为限制线和缩放这两种情况提供了解决方案。让我们来看一些详细解决这些问题的例子。

在图表上画一条界限线

另一个非常有用的特性是 CanvasOverlay 插件。它能让你在图表上画水平线和垂直线,目的是指示一个极限、一个阈值、一个截止日期或划定一个特定的范围。这可以通过在网页中包含 CanvasOverlay 插件来实现:

<script type="text/javascript"

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

通过包含这个插件,您在options : canvasOverlay中有了一个新对象。在这个对象中,您将使用对象的属性定义一个对象数组。这些对象中的每一个都将由绘制在画布上的一条线来表示,jqPlot 在画布上创建您的图表。在canvasOverlay中已经定义了五种类型的对象:

  • horizontalLine
  • verticalLine
  • dashedHorizontalLine
  • dashedVerticalLine
  • Line (generic)

要查看如何在图表中插入这些限制线,让我们从一个简单的折线图开始,在该折线图中,您希望显示两条不同颜色的水平限制线:一条红线标记上限,一条蓝色虚线标记下限。

在清单 9-35 中,你定义了两个对象:一个是下限的horizontalLine,一个是上限的dashedHorizontalLine。一旦定义了这两条线,就必须指定它们的属性。它们的属性,比如 y 值、lineWidthcolor,其含义是显而易见的。lineCap属性指定放置在线条上的结束类型;可以是roundbuttsquare

清单 9-35。ch9_15.html

$(document).ready(function(){

var data = [100, 110, 140, 130, 80, 75, 120, 130, 100];

var options = {

canvasOverlay: {

show: true,

objects: [

{horizontalLine: {

y: 70,

lineWidth: 3,

color: 'rgb(255, 0, 0)',

shadow: true,

lineCap: 'butt'

}},

{dashedHorizontalLine: {

y: 145,

lineWidth: 4,

color: 'rgb(0, 0, 255)',

shadow: false,

dashPattern: [8, 16],

lineCap: 'round'

}}

]

}

};

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

});

图 9-31 显示了在值 70 和 145 之间界定线图的两条极限线。

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

图 9-31。

A line chart with lower and upper limits

您已经看到了如何在两条界限线之间划定折线图,在更复杂的情况下(但不是在这种情况下,这种情况不太常见),可以方便地改变这些界限的值,从而能够随意移动它们,例如,通过单击一系列按钮。在下一个示例中,您将继续通过添加按钮来实现当前图表,这些按钮用于滑动图表表面上的限制线。

向图表添加按钮

使用前面的例子(见清单 9-35),你将看到如何在图表中添加按钮。按钮可以放在网页的任何部分,因为它们在画布之外。在这里,它们的功能是允许您随意移动限制线,只需单击它们即可。

为此,您需要四个按钮:两个用于向上移动限制线,两个用于向下移动限制线,标记如下:

  • 下限上限
  • 下限下降
  • 上限向上
  • 上限下降

你可以在网页的<body>部分的任何地方添加清单 9-36 中定义的四个按钮。

清单 9-36。ch9_16.html

<div>

<button onclick="lineup(myPlot, 'lowlimit')">Low Limit Up</button>

<button onclick="linedown(myPlot, 'lowlimit')">Low Limit Down</button>

</div>

<div>

<button onclick="lineup(myPlot, 'hilimit')">High Limit Up</button>

<button onclick="linedown(myPlot, 'hilimit')">High Limit Down</button>

</div>

这些行将生成如图 9-32 所示的四个按钮。

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

图 9-32。

The buttons added to the chart in order to move the limit lines

在第二章中,向您介绍了可以用作控件的 JQuery 用户界面库(jQuery UI)小部件。考虑到这种类型控件的潜力,建议使用库提供的按钮部件(关于如何使用这些部件的更多信息,参见第一章 5)。如果您希望使用 jQuery UI 小部件来替换这四个按钮,那么您需要包括以下插件:

<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css>

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

但是,如果您希望引用本地安装的库(参见附录 A),则必须包含以下代码:

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

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

并且,在 HTML 页面的<body>部分,添加清单 9-37 中的代码。

清单 9-37。ch9_16.html

<script>

$(function() {

$('button')

.button()

.click(function( event ) {

event.preventDefault();

});

});

</script>

按钮现在以 jQuery UI 风格显示(或者,更准确地说,以“平滑度”为主题,这是其中之一),如图 9-33 所示。

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

图 9-33。

The same four buttons, but displayed using the jQuery UI

此时,这些按钮完全处于非活动状态。你需要开发两个 JavaScript 函数;当按钮被按下时,这些将被执行。第一个函数lineup()将增加作为参数传递的线的 y 值(上限或下限),然后强制绘制新的图表。第二个函数linedown()将减少 y 值。这两个函数必须在 jQuery 函数$(document).ready()的外部(见清单 9-38)。

清单 9-38。ch9_16.html

function lineup(plot, name) {

var co = plot.plugins.canvasOverlay;

var line = co.get(name);

line.options.y += 5;

co.draw(plot);

}

functionlinedown(plot, name) {

var co = plot.plugins.canvasOverlay;

var line = co.get(name);

line.options.y -= 5;

co.draw(plot);

}

下一步是将由$.jqplot()函数返回的对象赋给一个变量:

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

Note

注意不要写varmyPlot,否则当你按下按钮时,你将看不到图表的任何变化。

最后一步是命名canvasOverlay对象中的两条线,如清单 9-39 所示。

清单 9-39。ch9_16.html

objects: [

{horizontalLine: {

name: 'lowlimit',

y: 70,

lineWidth: 3,

color: 'rgb(255, 0, 0)',

shadow: true,

lineCap: 'butt'

}},

{dashedHorizontalLine: {

name: 'hilimit',

y: 145,

lineWidth: 4,

color: 'rgb(0, 0, 255)',

shadow: false,

dashPattern: [8, 16],

lineCap: 'round'

}}

]

最后,你得到一个包含四个按钮的图表,如图 9-34 所示。

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

图 9-34。

A line chart with a set of buttons that vary the lower and upper thresholds

有时,您需要在图表中添加垂直线,尤其是当您必须标记截止日期时。在这种情况下,我们将像以前一样工作,但有一些不同。例如,假设您想要在折线图中放置一条竖线来表示截止日期。在这种情况下,您使用清单 9-40 中的代码。

清单 9-40。ch9_17.html

$(document).ready(function(){

var data = [100, 110, 140, 130, 80, 75, 120, 130, 100];

var options = {

canvasOverlay: {

show: true,

objects: [

{verticalLine: {

name: 'lowlimit',

x: 5,

lineWidth: 3,

color: 'rgb(50, 200, 50)',

shadow: true,

lineCap: 'butt',

yOffset: 0

}}

]

}

};

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

});

这一次,您将只需要两个按钮。

<div>

<button onclick="lineright(myPlot, 'lowlimit')">Postpone Deadline</button>

<button onclick="lineleft(myPlot, 'lowlimit')">Anticipate Deadline</button>

</div>

现在,您必须开发两个 JavaScript 函数,它们将在按钮被按下时水平移动界限线。像前面看到的 JavaScript 函数一样,清单 9-41 中的两个函数必须放在 jQuery 函数$(document).ready()的外部。

清单 9-41。ch9_17.html

functionlineright(plot, name) {

var co = plot.plugins.canvasOverlay;

var line = co.get(name);

line.options.x += 1;

co.draw(plot);

}

functionlineleft(plot, name) {

var co = plot.plugins.canvasOverlay;

var line = co.get(name);

line.options.x -= 1;

co.draw(plot);

}

结果就是图 9-35 中的图表,中间有一条绿色竖线。通过点击这两个按钮,如果你想提前,线条会向左移动,如果你想推迟,线条会向右移动。

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

图 9-35。

A line chart with a green horizontal limit line

变焦

通常,当您处理大量数据时,图表上会出现一条由数千个点组成的线。正是在这种情况下,变焦功能是必不可少的。从宏观视图开始,您可以放大线的一部分以获得数据的微观视图。

光标插件还支持绘图缩放功能。通过在图上单击并拖动光标,可以放大和滚动图表的小部分。如果双击,可以全部重置,回到宏观视图。因此,您需要在您的 web 页面中包含光标插件,并且因为您在 x 轴上有日期值,所以还必须包含 DateAxisRenderer 插件:

<script type="text/javascript"

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

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

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

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

/jqplot.dateAxisRenderer.min.js"></script>

<script type="text/javascript"

src="http://cdn.jsdelivr.net/jqplot/1.0.8/plugins/jqplot.cursor.min.js

清单 9-42 说明了大量输入数据的可用性。

清单 9-42。ch9_18.html

var data = [["6/22/2012 10:00:00", 110.32], ["6/8/2012 10:00:00", 115.84],

["5/26/2012 10:00:00", 121.23], ["5/11/2012 10:00:00", 122.12],

["4/27/2012 10:00:00", 120.69], ["4/13/2012 10:00:00",123.24],

["3/30/2012 10:00:00", 116.78], ["3/16/2012 10:00:00", 115.16],

["3/2/2012 10:00:00", 113.57], ["2/17/2012 10:00:00", 120.45],

["2/2/2012 10:00:00", 121.28], ["1/20/2012 10:00:00", 124.7],

["1/5/2012 10:00:00", 130.07], ["12/22/2011 10:00:00", 129.36],

["12/8/2011 10:00:00", 130.76], ["11/24/2011 10:00:00", 133.96],

["11/10/2011 10:00:00", 140.02] ,["10/27/2011 10:00:00", 138.36],

["10/13/2011 10:00:00", 140.54], ["9/29/2011 10:00:00", 140.91],

["9/15/2011 10:00:00", 140.15], ["9/2/2011 10:00:00", 138.25],

["8/25/2011 10:00:00", 137.29], ["8/11/2011 10:00:00", 139.15],

["7/28/2011 10:00:00", 144.86], ["7/14/2011 10:00:00", 145.32],

["6/30/2011 10:00:00", 148.12], ["6/16/2011 10:00:00", 146.43],

["6/2/2011 10:00:00", 147], ["5/19/2011 10:00:00", 144.62],

["5/5/2011 10:00:00", 143.2], ["4/21/2011 10:00:00", 144.06],

["4/7/2011 10:00:00", 137.45], ["3/24/2011 10:00:00", 138.08],

["3/10/2011 10:00:00", 137.92], ["2/25/2011 10:00:00", 131.18],

["2/11/2011 10:00:00", 129.64], ["1/28/2011 10:00:00", 133.9],

["1/14/2011 10:00:00", 134.25], ["12/31/2010 10:00:00", 137],

["12/17/2010 10:00:00", 136.69], ["12/3/2010 10:00:00", 144.87],

["11/19/2010 10:00:00", 146.7], ["11/5/2010 10:00:00", 143.97],

["10/22/2010 10:00:00", 139.6], ["10/8/2010 10:00:00", 133.39],

["9/24/2010 10:00:00", 130.27], ["9/10/2010 10:00:00", 132.75],

["8/27/2010 10:00:00", 130.25]];

启用缩放功能非常简单。只需在options中将zoom属性设置为'true',如清单 9-43 所示。

清单 9-43。ch9_18.html

var options = {

series: [{

neighborThreshold: -1

}],

axes:{

xaxis:{

renderer: $.jqplot.DateAxisRenderer,

min:'August 1, 2010 16:00:00',

tickInterval: '6 months',

tickOptions: {formatString: '%#m/%#d/%Y'}

}

},

cursor:{

show: true,

zoom: true,

showTooltip: false

}

};

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

或者,如果您愿意,您可以禁用重置缩放的双击。光标插件还通过在外部使用resetZoom()方法来扩展绘图对象(由$.jqplot()函数返回的值)。此外,可以从用户代码或另一个 HTML 元素(如按钮)调用此方法来重置绘图缩放。

您可以在 jQuery ready()函数中定义这个函数:

$('.button-reset').click(function() { myPlot.resetZoom() });

然后,在您想要的任何位置插入以下行,以便将按钮放在网页的<body>部分:

<button class="button-reset">Reset Zoom</button>

图 9-36 提供了代表不同时刻的折线图的一系列图片。第一张图片是从浏览器显示的折线图,没有任何缩放。第二张图片显示了用户选择的图表区域,目的是进行缩放。最后一张图片展示了这种缩放的结果。如果用户单击重置缩放按钮,浏览器将再次显示第一张图片。

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

图 9-36。

A detail of the line chart extracted by zooming

更改图表外观

多亏了它的几个插件,jqPlot 可以直接在画布上呈现图表组件,包括文本。到目前为止,很明显 jqPlot 库的亮点是可以通过改变 jqPlot 属性和添加的插件的默认值来改变任何图表元素的外观。但是,这不是实现这种改变的唯一方法。如果您想要修改 HTML 页面中元素的外观,您可以求助于 CSS 样式。即使对于 jqPlot 元素也是如此。

可以用 CSS 类引用几个(但不是全部)jqPlot 对象,以改变这些对象的样式,而不必在options中设置它们的属性。对象可以由 CSS 定制,使用 CSS 类,如.jqplot-*

自定义文本,使用 CSS

jqPlot 库提供了 CSS 类,使用这些类,您可以在不引用options对象的情况下更改一些属性。举例来说,您将使用其中一些类来更改图表中的文本。让我们从实现一个简单的多系列折线图开始,它只有一个标题和一个在options中定义的轴标签(见清单 9-44)。

清单 9-44。ch9_10a.html

$(document).ready(function(){

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

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

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

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

var options = {

title: 'Multiseries Line Chart,

axesDefaults: {

label: 'Axis Label'

}

};

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

});

您在清单 9-45 中添加了<style>部分,它可以被提取为一个 CSS 文件。

清单 9-45。ch9_10a.html

<style>

.jqplot-title {

font-family: "Arial Black";

font-size: 24px;

color: lightblue;

}

.jqplot-xaxis-label {

font-size: 24px;

}

.jqplot-axis {

font-family: "Arial";

font-size: 16px;

}

.jqplot-xaxis {

color: green;

}

.jqplot-yaxis {

color: orange;

font-weight: bold;

}

</style>

图 9-37 展示了清单 9-45 中 CSS 样式设置前后的情况,让我们看到了所做的改动。

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

图 9-37。

Some CSS styles applied to the tick labels and title

更改背景颜色

继续上一个例子(见清单 9-45),你现在发现只要简单地添加一个属性到options(在清单 9-46 中突出显示),你就可以获得一个黑色背景。如图 9-38 所示。

清单 9-46。ch9_10b.html

var options = {

title: 'Multiple Data Arrays',

axesDefaults: {

label: 'Axis Label'

},

grid: {

background: '#000000'

}

};

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

图 9-38。

A line chart with a black background

使用 CSS 进一步定制

这一次,您不仅要更改网格的背景,还要更改图表周围的空间,使其更具吸引力。您可以通过将 CSS 样式直接应用于图表元素来实现这一点。

例如,与默认设置(灰色网格,白色背景)相反,假设您决定将图表放置在全黑背景上。在这种情况下,您需要创建一个容器,在myChart目标(另一个<div>元素)中包含一个<div>元素:

<div class="chart-container">

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

</div>

该容器用于扩展将放置黑色背景的区域;我们通过用chart-container设置其类来引用容器。

此时,要记住的最重要的事情是,容器和目标这两个元素现在可以通过更改它们的 CSS 样式来适当地表征。这可以通过为.chart-container指定属性来实现,如清单 9-47 所示(至于目标的元素,已经使用.jqplot-*类设置好了)。在.chart-container类中,你将background属性设置为'black';容器的大小由widthheight属性确定。您还可以使用padding属性来更好地将目标放在容器的中心。填充清除元素内容周围的区域,扩展其背景色。这四个值分别是顶部、右侧、底部和左侧填充。

清单 9-47。ch9_20.html

<style type="text/css">

.chart-container {

background : #000000;

padding: 30px 0px 80px 30px;

width: 560px;

height: 330px;

}

...

</style>

容器和目标的 CSS 定制的组合结果如图 9-39 所示。

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

图 9-39。

A multiseries line chart with a black background

设置网格

默认情况下,图表的网格是灰色的。然而,在前面的例子中(见清单 9-47),你看到了如何通过设置optionsgrid对象的属性来改变网格。在本例中,您将继续修改相同的多系列折线图,但这一次,您将关注网格属性。

您可以更改网格颜色和厚度。例如,您可能想要一个黑色的网格,增加厚度,在这种情况下,您必须定义gridLineColorgridLineWidth属性。此外,有时,默认情况下,jqPlot 可能会用太粗的网格显示图表,这可能会妨碍而不是有助于可读性。在这种情况下,您需要减少分笔成交点的数量。这很容易做到,通过以特定的方式为options中的axes对象内的每个轴设置numberTicks属性。清单 9-48 包括了所有这些变化。

清单 9-48。ch9_10d.html

var options = {

title: 'Multiseries Line Chart',

轴任务:{

label: 'AxisLabel'

}

grid: {

background: '#000000'

gridLineColor: '#ffffff',

gridLineWidth: 2

},

axes: {

xaxis: {

numberTicks: 5,

min: 0,

max: 8

},

yaxis: {

numberTicks: 3,

min: 0,

max: 12

}

}

};

最后,你会得到一个带有所需网格的新图表(见图 9-40 )。

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

图 9-40。

A multiseries line chart with a customized grid

请注意,有一个灰色的轮廓界定了图表:边界。你也可以改变它,或者禁用它,如清单 9-49 所示。您也可以将drawBorder属性设置为'false'并禁用shadow

清单 9-49。ch9_20e.html

grid: {

drawBorder: false,

shadow: false,

gridLineColor: '#000000',

gridLineWidth: 2,

},

在这些改变之后,你获得了一个可读性更好的网格,如图 9-41 中的网格,它有一个与网格颜色相同的边框(白色)。

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

图 9-41。

A more readable multiseries line chart, with a customized grid

使用折线图上的区域

到目前为止,您已经看到了折线图基本上由线条连接的点集合组成,描述了一定规模的趋势。现在,你可能会发现这样的观点有些局限。通常,折线图最有趣的部分是一条线(或几条线)以某种方式划定的区域。

面积图

折线图可以转换成面积图。在这个例子中,你将使用你已经创建的多系列折线图(见清单 9-50),为了得到一个混合了区域和线条的新图表,你需要进行一些修改。在这里,您将看到只需做很少的更改就能达到预期的效果。

清单 9-50。ch9_22a.html

$(document).ready(function(){

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

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

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

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

var options = {

title:'Multiple Data Arrays'

};

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

});

首先,在options中,您必须将fill属性插入到您想要表示为区域的系列中。这一次,您选择seriesDefaults将区域表示应用于所有系列。这样,你就得到一个面积图。为了使图表更好,你可以添加其他选项,比如平滑(见清单 9-51)。

清单 9-51。ch9_22a.html

var options = {

title: 'Multiple Data Arrays',

seriesDefaults: {

showMarker: false,

rendererOptions: {

smooth: true

},

fill: true

}

};

但是,当运行该网页时,您立即发现有些不对劲:最后一个系列的区域覆盖了其他系列(见图 9-42 )。

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

图 9-42。

An area chart with one series covering the others

在对序列应用fill属性之前,您需要考虑序列中相应区域的表示顺序。在这种情况下,只需以不同的方式对序列进行排序:

$.jqplot ('myChart', ``data4, data3, data2, data1

结果是一个精确的面积图,如图 [9-43 所示。

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

图 9-43。

A multiseries area chart rendered correctly

折线图和面积图

在同一个图表中混合线条和区域也可以创造出非常好的效果。继续上一个例子(见清单 9-51),不要在seriesDefaults中设置fill属性,然后对所有序列应用填充,你可以决定逐个序列地这样做,以选择哪个序列必须表示为面积,哪个序列必须表示为线条,如清单 9-52 所示。

清单 9-52。ch9_22b.html

var options = {

title:'Multiple Data Arrays',

seriesDefaults: {

showMarker: false,

rendererOptions: {

smooth: true

}

},

series: [{}, {fill: true}, {}, {fill: true}]

};

图 9-44 显示了如何组合折线图和面积图。

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

图 9-44。

A combined line and area chart

波段图

带状图(也称为高低点折线图或范围图)是一种结合了面积图和折线图特征的图表。

带状图是一种增强了底层阴影区域的折线图(见图 9-45 )。该区域代表 y 轴上数值范围的上限和下限。这个范围随着 x 的变化而变化,最终,你会得到一个波段。

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

图 9-45。

A band chart

您可以使用一个带区来指示 y 轴上的特定区间,该区间随 x 轴上的值而变化,并与其内部的线条趋势相关联,例如,说明置信区间或误差带。另一个用途可能是突出显示随时间变化的分布和显示算术平均值的线。

使用 jqPlot,可以自动计算或手动分配波段。如果手动分配,则必须以两个[x,y]值数组的形式提供波段的边界。第一个数组限定了下限线;第二个数组,上界线。这两个数组作为另一个数组的两个元素连接在一起,传递给options中的bandData属性。

首先,让我们用[x,y]值对和波段数组bdata定义一个数据数组,包含两个数组:下界线和上界线(见清单 9-53)。

清单 9-53。ch9_24a.html

var data         = [[10,100],[20,110],[30,140],[40,130],

[50,80],[60,75],[70,120],[80,130],[90,100]];

varbdata =[ [[10,90],[20,100],[30,130],[40,120],

[50,70],[60,65],[70,110],[80,120],[90,90]],

[[10,110],[20,120],[30,150],[40,140],

[50,90],[60,85],[70,130],[80,140],[90,110]]];

然后,在options中,使用清单 9-54 所示的代码。图 9-46 展示了由此产生的带状线图。

清单 9-54。ch9_24a.html

var options = {

series: [{

rendererOptions: { bandData: bdata }

}],

seriesDefaults: {

shadow: false,

showMarker: false

}

};

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

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

图 9-46。

A banded-line chart

如果您选择绘制平滑折线图,波段也会变得平滑。清单 9-55 给出了代码,图 9-47 给出了结果。

清单 9-55。ch9_24b.html

rendererOptions: {

bandData: bdata,

smooth: true

}

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

图 9-47。

A smooth-banded-line chart

波段数据数组中的点数不必与数据序列中的点数相对应。此外,如果数据系列经过平滑处理,波段数据将被绘制为平滑线。该带不必相对于主线对称。通过在bdata数组中插入一个具有不对称 y 值的数组,可以使波段不对称,如清单 9-56 所示。

清单 9-56。ch9_24c.html

varbdata =[ [[10,90],[30,100],[40,100],[50,70],

[60,65], [70,110],[80,120],[90,90]],

[[10,110],[30,150],[40,140],[50,120],

[60,85], [70,130],[80,140],[90,110]] ];

现在,图 9-48 中的带相对于主线不对称。

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

图 9-48。

A nonuniform banded-line chart

但是,提供波段数据不是强制性的;它们可以由 jqPlot 自动计算。要激活这个特性而不使用任何数组,你必须将bands对象的show属性设置为rendererOptions中的'true',如清单 9-57 所示。如图 9-49 所示,默认情况下,波段间隔覆盖主线 y 值的+/-3%。

清单 9-57。ch9_24d.html

series: [{

rendererOptions: {

bands: { show: true},

smooth: true

}

}],

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

图 9-49。

A banded-line chart with a band interval of +/-3 percent

填充折线图中的线条

你刚刚了解了乐队。为什么不填充两条系列线之间的区域?甚至这个任务也可以用 jqPlot 来完成。通过设置fillBetween对象内的属性,可以控制图上两条线之间的区域。

在这里,你从一个非常简单的多系列折线图开始(同样的例子用于其他情况),如清单 9-58 所示。

清单 9-58。ch9_5a.html

$(document).ready(function(){

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

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

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

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

var options = {

title:'Multiple Data Arrays',

};

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

});

使用此多系列折线图,您可以考虑每个系列,第一个系列的索引从 0 开始;1、为第二系列;2、为第三;诸如此类。

所以,如果你想填充两行之间的区域,你需要在series1series2属性中指定与它们对应的两个索引。例如,如果你想填充第二个和第四个系列之间的区域,你必须设置series1为 1(第二个系列)和series 2为 3(第四个系列),如清单 9-59 所示。可选地,您可以使用color属性或者更好地使用rgba()函数来设置分隔区域的颜色。

清单 9-59。ch9_25.html

var options = {

title: 'Multiple Data Arrays',

fillBetween: {

series1: 1,   //second series

series2: 3,   //fourth series

color: "rgba(10, 120, 130, 0.7)"

}

});

在图 9-50 中,您可以看到第二个和第四个系列之间的选定区域是彩色的。

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

图 9-50。

A multiseries line chart with a colored area between two lines

您可以将一个 JavaScript 函数绑定到一个按钮,用于更新每个系列的绘图设置,然后重新绘制所有内容。为此,让我们将清单 9-60 中的函数添加到 jQuery ready()函数中。

清单 9-60。ch9_26.html

$("button[name=changeFill]").click(function(e) {

plot1.fillBetween.series1 = parseInt($("input[name=series1]").val());

plot1.fillBetween.series2 = parseInt($("input[name=series2]").val());

plot1.replot();

});

为了让前面的 JavaScript 函数工作,您需要将函数$.jqplot()返回的值赋给变量plot1:

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

并且,在网页的<body>部分,你必须添加两个输入文本区域和一个按钮,如清单 9-61 所示。

清单 9-61。ch9_26.html

<label for="series1">First Series: </label>

<input type="text" name="series1" value="1" />

<label for="series2"> Second Series: </label>

<input type="text" name="series2" value="3" />

<button name="changeFill">Change Fill</button>

结果如图 9-51 所示。

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

图 9-51。

A multiseries line chart with a selectable colored area

要用一个 jQuery UI 小部件替换简单的 HTML 控件,你必须对代码做一些修改以便集成它,如清单 9-62 所示。

清单 9-62。ch9_26ui.html

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

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

...

$("button[name=changeFill]").click(function(e) {

plot1.fillBetween.series1 = parseInt($("#combobox").val());

plot1.fillBetween.series2 = parseInt($("#combobox2").val());

plot1.replot();

});

...

<div class="ui-widget">

<label>First Series : </label>

<select id="combobox">

<option value="0">1</option>

<option value="1">2</option>

<option value="2">3</option>

<option value="3">4</option>

</select>

</div>

<div class="ui-widget">

<label>Second Series : </label>

<select id="combobox2">

<option value="0">1</option>

<option value="1">2</option>

<option value="2">3</option>

<option value="3">4</option>

</select>

</div>

<button name="changeFill">Change Fill</button>

<script>

$(function() {

$( 'button')

.button()

.click(function( event ) {

event.preventDefault();

});

});

</script>

结果就是图 9-52 所示的图表。

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

图 9-52。

A multiseries line chart with a selectable colored area

趋势线

jqPlot 真的是惊喜满满。除了你已经看到的关于折线图的所有东西,jqPlot 还可以计算和表示趋势线。这些通常是在图表中绘制的直线,但有时它们可以是指数的(如果它们在对数标度中是线性的)。趋势线表示图表中绘制的系列数据的一般模式或方向。这条线是用统计技术画出来的。这个功能由另一个插件执行:Trendline。

要启用此功能,您需要在网页中包含插件:

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

之后,您只需要激活插件,添加启用它的行,如清单 9-63 所示。

清单 9-63。ch9_27a.html

$(document).ready(function(){

var data = [100, 110, 140, 130, 135, 132, 140, 135, 142]

$.jqplot.config.enablePlugins = true;

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

});

用这几条线可以得到一条趋势线,如图 9-53 所示。

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

图 9-53。

The linear trend line of a line chart

但是,如果您喜欢显式地表达这些属性,您可以通过使用选项来实现。这使您能够像处理图表中的其他对象一样处理趋势线。假设你想改变线条的颜色,增加线条的粗细,使其更加突出(见清单 9-64)。

清单 9-64。ch9_27b.html

var options = {

seriesDefaults: {

trendline: {

show:true,

color: '#ff0000',

lineWidth: 4

}

}

}

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

你现在对趋势线有了更多的控制。图 9-54 显示了trendline对象中带有属性设置的线条(趋势线较粗,在浏览器上显示为深红色)。

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

图 9-54。

A customized linear trend line in a line chart

如前所述,可以使用趋势线曲线,它表示图表中各点之后的指数趋势。让我们在清单 9-65 的下一个例子中检验这一点。

清单 9-65。ch9_28.html

$(document).ready(function(){

var data = [[10, 1.44], [30, 6.98], [50, 10.7], [70, 37.5], [90, 78.1]];

var options = {

seriesDefaults: {

trendline: {

show:true,

color: '#ff0000',

lineWidth: 4,

type: 'exponential'

}

}

}

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

});

图 9-55 显示了一个折线图,其中绘制了一系列遵循指数趋势的点。因此,你可以用指数趋势线来强调这一点。

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

图 9-55。

A customized exponential trend in a line chart

摘要

在这丰富的章节中,你已经精通 jqPlot 世界。您看到了这个库提供的许多可能性,使您能够尽最大能力实现折线图。您学习了如何操作绘制图表的基本元素,如轴和刻度。特别是,您看到了如何在同一个图表中管理多个数据系列(多系列图表),添加各种图形效果。您还探索了 jqPlot 库允许您操作不同格式的日期和时间值的方式。此外,您看到了如何使用 HTML 格式定制一些元素,以及突出显示数据点。在本章的最后一部分,你处理了更复杂的情况,比如生成趋势线和使用波段图。

在下一章,一个充满争论的章节,你将会面对其他新的概念,这次是应用在条形图上。