Легко и просто: графики и диаграммы на веб-страницах. Часть 2

Я продолжаю начатый в прошлой статье рассказ о том, как с помощью javascript без использования “тяжелых” серверных скриптов на php или flash- роликов создавать и внедрять в html-странички картинки диаграмм и графиков. Сегодня я завершу повествование об основных функциях библиотеки flot и посвящу материал всевозможным “красивостям”, т.е. тому, как можно управлять внешним видом серий данных на диаграмме, настраивать внешний вид легенды диаграммы и ее фона.

В прошлой статье я показал заготовку диаграммы: создал несколько массивов с числовыми данными (серии данных) и рассказал о том, как настроить диаграмму с данными, расположенными вдоль временной шкалы. Сейчас я покажу, как создать диаграмму с двумя осями OX или OY. Для этого трюка не требуется никаких специальных действий по подготовке серий данных – это все те же массивы пар чисел “x” и “y”. Единственное, что я должен сделать, так это при вызове функции plot указать для каждой из серий данных то, к какой оси она относится. Итак, первым шагом я создаю два массива данных с данными для графика функций y=sin(x) и y=exp(x). Учитывая, что значения первой функции колеблются от -1 до +1, а значения экспоненты возрастают очень быстро, то очевидно, что одновременно эти два графика можно разместить на одной диаграмме только если привязать их к разным масштабам оси OY. Итак, первый шаг – подготовка данных:

var seriesSin = [];
var seriesExp = [];
var i = 0;
for (var i = -Math.PI/2; i <<< +Math.PI/2; i+=0.1){
seriesSin.push ( [i, Math.sin(i)] );
seriesExp.push ( [i, Math.exp(i)] );
}

Завершающий шаг при вызове функции построения графика plot – это указать для каждой из серии данных то, к какой оси OY она относится. И служит для этого свойство “yaxis”, принимающее два значения “1” (значение по-умолчанию) или “2” (результат см. на рис. 1):

var chartConfig = {};
$.plot( $("#placeholder"),
[
{ data: seriesSin, label: "sin (x)", yaxis: 1, xaxis: 1 },
{ data: seriesExp, label: "exp (x)", yaxis: 2 , xaxis: 1},
], chartConfig);

Иногда случается ситуация, когда значения двух серий данных сильно отличаются не только по значениям оси OY, но и по значениям оси OX. В этом случае мы можем на одной диаграмме привязать эти серии данных не только к двум различным осям OY, но и к двум различным осям OX (очевидно, что вторая ось OX будет расположена вверху рисунка). И как было показано на примере выше, за “привязку” серии данных к определенной оси OX отвечает свойство “xaxis”. Следующее улучшение диаграммы – декоративное. Т.к. значения по оси OX откладываются в радианах, то я хотел бы изменить правило, по которому flot будет рисовать вертикальные линии-отметки (tick) и ставить подписи по оси OX. Первое изменение заключается в том, что большое количество отметок для графика sin(x) не нужно – достаточно показать, где находится значение числа –Pi/4, затем “0” и числа +Pi/4. Еще одним улучшением будет назначение специальной функции форматирования надписи tick’а перед выводом. Отвечать за это будет функция “formatRad”, в которой я округлю выводимое значение до тысячных долей. Как всегда, для того, чтобы изменить параметры внешнего вида диаграммы, я работаю с объектом chartConfig:

var chartConfig = {
xaxis: {tickFormatter: formatRad, ticks: [-Math.PI/4, 0, Math.PI / 4]}
}
// а теперь функция форматирования подписей по оси OX
function formatRad (value){
return Math.round(value*1000)/1000;
}

То, что у меня получилось, показано на рис. 1, где прошу обратить внимание на подписи к оси OX. С равной долей успеха из функции форматирования tick’а можно вернуть небольшой кусочек текста с html-форматированием. Так, в следующем примере я поместил надпись tick’а внутрь прямоугольника с красным цветом границ:

function formatRad (value){
return '<<
>>'+value+'<<
>>';
}

В том случае, если количество линий-отметок невелико, можно использовать прием с явным перечислением списка значений оси OX, при которых нужно ставить отметку. И каждому значению tick’а можно присвоить текст надписи, который выводится рядом с линей tick’а. В следующем примере я решил вывести три точки отметки –Pi/4, затем 0 и +Pi/4. Но в отличие от предыдущего примера, где количество радиан перед выводом на экран просто округлялось до тысячи, теперь мне хочется вывести подписи, содержащие изображение числа p (Unicode код символа p равен ‘\u03c0'):

var chartConfig = {
xaxis: {tickFormatter: formatRad,
ticks: [[-Math.PI/4, "-\u03c0/4"], 0, [+Math.PI/4, "+\u03c0/4"]]
}
}

Следующая часть материала будет посвящена различным “красивостям”, т.е. я расскажу о том, как можно изменять внешний вид линий диаграммы и ее легенды. Наиболее часто изменяемая настройка внешнего вида диаграммы – это цвет линии, ее толщина и наличие точек-отметок, соответствующих значениям серий данных.

var chartConfig = {
points: { show: true, radius: 5, lineWidth: 2, fill: true, fillColor: '#ffffff' },
lines: {show: true, lineWidth: 3},
shadowSize: 4,
colors: ['#ff0000', '#00ff00']
};

Первое, что я изменил - это цвет линии графика, и служит для этого свойство “colors”. Colors – это массив из стольких элементов, сколько серий данных мы хотим показать на диаграмме. И каждый элемент этого массива – цвет линии данных с соответствующим номером. Для того чтобы видеть не просто линию, а то, какие значения эта линия соединяет между собой, нужно настроить свойство “points”. Так, я включил отображение точек-маркеров с помощью свойства “show”, задал им радиус, затем толщину линий и цвет, которым кружки-маркеры будут закрашены внутри. Если включить отображение точек, то flot тут же выключит отображение линий, соединяющих точки между собой. Поскольку я хотел бы видеть на диаграмме и точки-маркеры, и соединяющие их линии, то я должен настроить свойство “lines”. Помимо показанных в примере характеристик “show” и “lineWidth”, есть пара забавных конфигурационных параметров “fill” и “fillColor”. Применять их имеет смысл очень аккуратно, т.к. если режим заливки включен, flot закрасит области диаграммы, находящиеся между осью OX и линией функции (см. рис. 2).

Если у нас есть несколько пересекающихся линий с сериями данных, то заливка их всех может сделать диаграмму нечитаемой. Следующее свойство позволит полностью изменить внешний вид диаграммы, превратив ее в столбчатую – “bars”. Вот основные характеристики, управляющие внешним видом столбчатой диаграммы. Во-первых, это “show” для включения отображения столбцов, затем с помощью “lineWidth” мы задаем толщину линий (в пикселях). А что касается ширины собственно столбца, то за это отвечает параметр “barWidth”. Вот только задается его значение в отличие от “lineWidth” или свойства, управляющего радиусом “radius” кружков- маркеров, не в пикселях, а в относительных единицах измерения. Так, в следующем примере я задал ширину столбца в 0.05 единицы (радиана). А учитывая, что ранее, когда я формировал массив с данными для графика функции y=sin(x), использовался шаг приращения “x”, равный 0.1,то, как и показано на рис. 3, размер столбика будет составлять ровно половину размера шага функции.

var chartConfig = {
points: { show: true, radius: 10, lineWidth: 2, fill: true, fillColor: '#ffffff' },
lines: {show: true, lineWidth: 3 , fill: false, fillColor: '#ca0000' },
bars: {show: true, lineWidth: 1, barWidth: 0.05, fill: true,fillColor: '#ca0000' , align: "center" },
};

Внимательно посмотрите на рис. 3. Вы увидите, как центр столбика диаграммы совпадает с центром кружка-маркера. Для того чтобы управлять способом выравнивания столбика со значением серии данных либо по центру, либо по левому краю, я в примере выше указал для характеристики “align” значение “center”. Внимательный читатель уже задумался, что описанные выше характеристики внешнего вида диаграммы lines, bars, points носят “слишком глобальный характер” и применяются ко всем сериям данных. А есть ли способ для каждой серии данных индивидуально указать то, как она должна выглядеть? Например, совместить на одной диаграмме серию данных в виде линии, а вторую серию показать в виде набора столбиков? Да можно. Так, когда я вызываю функцию построения диаграммы plot и передаю вторым параметром массив с сериями данных, для каждой серии можно указать свой индивидуальный набор характеристик lines, points, bars. Результаты выполнения следующего кода показаны на рис. 4:

// никаких глобальных настроек диаграммы
var chartConfig = {};
// а теперь настраиваем каждую серию индивидуально
$.plot( $("#placeholder"), [
// первая серия данных в виде столбиков
{ data: seriesSin, label: "sin (x)", yaxis: 1, xaxis: 1, bars: {show: true, lineWidth: 1, barWidth: 0.05, fill: true,fillColor: '#ca0000' , align: "center" }},
// а вторая линией
{ data: seriesExp, label: "exp (x)", yaxis: 2 , xaxis: 1,
points: { show: true, radius: 5, lineWidth: 2, fill: true, fillColor: '#ffffff' },
lines: {show: true, lineWidth: 3 , fill: false, fillColor: '#ca0000' }},
], chartConfig);

Теперь рассмотрим то, какие возможности есть в flot для управления внешним видом легенды диаграммы. Прежде всего, мы можем включать и отключать показ легенды с помощью свойства “show”. Если легенда диаграммы показывается, то мы можем настроить ее местоположение с помощью свойства “position”. Значения для “position” кодируются двумя буквами, обозначающими края света. Так, комбинация “sw” (south, west) задает положение легенды в нижнем левом краю диаграммы. Для простого управления внешним видом легенды можно использовать работающие в паре свойства “backgroundColor” и “backgroundOpacity”. Они задают, соответственно, фоновый цвет прямоугольника, содержащего легенду диаграммы, и степень прозрачности этого фона (значения от 0 до 1). Для того чтобы получить полный контроль над внешним видом подписи к серии данных диаграммы, то я могу указать специальную функцию форматирования. В следующем примере я решил вывести текст названия серии данных в виде курсива:

function formatLabel (value){
return '<<>>'+value+'<<>>';
}
var chartConfig = {
legend: {show: true, position: "sw", backgroundColor: "#00ff00", backgroundOpacity: 0.5, labelBoxBorderColor: "#caca00", labelFormatter: formatLabel}
}

Иногда возникает потребность в размещении легенды не внутри диаграммы, а где-нибудь в другом месте html-страницы, например, под диаграммой. Или содержимое страницы представляется в виде таблицы из двух колонок. Так, первая колонка будет содержать диаграмму, а вторая - ее легенду. В любом случае, разработчики flot предусмотрели способ создать html-блок “заглушку” для последующего размещения в ней легенды диаграммы:

<<
>> Это заглушка для легенды <<
>>

А при настройке внешнего вида диаграммы я могу подсказать flot о том, что легенду нужно поместить именно внутрь блока “legend”:

var chartConfig = {
legend: {show: true, container : $('#legend') } }

Последняя характеристика диаграммы, связанная только с управлением ее внешним видом – это “grid”. Если внимательно присмотреться к любому из трех показанных выше рисунков диаграмм, то вы увидите на них набор вертикальных и горизонтальных линий как на бумаге “миллиметровке” – это и есть grid. Самое главное то, что, помимо настройки внешнего вида диаграммы, “grid” является “точкой доступа” к еще одному большому пласту функций, предусмотренных flot. Я говорю о средствах, позволяющих добавить к диаграмме немного интерактивности, т.е. возможности реагировать на действия пользователя. В следующем примере я, во-первых, изменен фоновый цвет сетки диаграммы на темно-серый (если же свойство “backgroundColor” будет равно не значению цвета, а специальному обозначению “null”, то фоновый цвет диаграммы будет прозрачным). Еще я поменял цвет линий контура “сетки” диаграммы на синий. И самое важное, я включил режим интерактивности диаграммы (autoHighlight). Теперь при наведении мыши на какое-либо из значений диаграммы (точнее, при попадании мыши в радиус mouseActiveRadius) flot подсветит кружок со значением.

var chartConfig = { grid: {
color: "#0000ff",
backgroundColor: '#5a5a5a',
tickColor: "#dddddd",
clickable: true,
hoverable: true,
autoHighlight: true,
mouseActiveRadius: 15
} };

То, что у меня получилось, показано на рис. 5. Но мне мало простой подсветки – я хочу создать собственную функцию, которую flot будет вызывать, извещая о каждом действии пользователя. Т.к. flot построен на базе и в соответствии с идеологией jquery, то для того, чтобы “привязать” функцию обработки события “ мышь наведена на диаграмму”, я использую унифицированный прием с вызовом jquery функции bind. Первым параметром для которой я передаю ссылку на объект графика (тот самый блок div, играющий роль “холста” для диаграммы). Второй и третий параметры функции bind – это название события, которое я хочу “слушать”, и ссылка на функцию-слушатель события соответственно.

$("#placeholder").bind("plothover", function (event, pos, item) {
$("#tooltip").remove();
if (! item) return;
var x = item.datapoint[0].toFixed(2);
var y = item.datapoint[1].toFixed(2);
var label = x + " = " + y + " ["+item.series.label+"]";
showTooltip(item.pageX, item.pageY, label);
});

Обработчик события “plothover” вызывается при каждом движении мыши над диаграммой, и каждый раз внутри свойства pos содержится информация о координатах мыши. Что очень важно, задаются эти координаты так, чтобы соответствовать сериям данных диаграммы. Т.е. если ось OX диаграммы изменяется в отрезке от –P/2 до +P/2, то и координаты мыши меняются в этом же диапазоне. Если же вас заинтересовали координаты мыши в абсолютном счислении, т.е. измеряемые в пикселях и с центром отсчета в левом верхнем углу окна браузера, то не стоит забывать, что “холст” для рисования диаграммы – это всего лишь обычный блок “div”. Для которого мы можем приказать ловить “классическое” событие перемещения мыши:

$("#placeholder").bind("mousemove", function (event) {
alert (event.clientX + ", "+ event.clientY);
} );

Возвращаясь назад к задаче показа на диаграмме всплывающих подсказок. Первым делом, внутри функции обработки события “plothover” я проверил, была ли эта функция вызвана в случае, когда курсор мыши попал в активную зону одного из кружков-маркеров диаграммы. Критерием попадания является то, что переменная “item” (хранящая пару значений x, y) не равна null. После того, как я извлек из item пару значений “x, y” и сформировал строку надписи для всплывающей подсказки, остается только создать эту самую всплывающую подсказку. Не мудрствуя лукаво, я скопировал в одном из примеров идущих в поставке flot (пример называется “interacting”) код функции, создающей в заданных координатах всплывающую подсказку:
function showTooltip(x, y, contents) {
$('<<
>>' + contents + '<<
>>').css( {
position: 'absolute',
display: 'none',
top: y + 5, left: x + 5,
border: '1px solid #fdd',
padding: '2px',
'background-color': '#fee',
opacity: 0.80
}).appendTo("body").fadeIn(200);
}

То, что у меня получилось, показано на рис. 5. По аналогии с событием “hover” вы можете создать свою функцию, отслеживающую клики пользователя по диаграмме. Как это сделать, можно подсмотреть в файлах примеров flot.

black-zorro@tut.by black-zorro.com


Компьютерная газета. Статья была опубликована в номере 29 за 2009 год в рубрике программирование

©1997-2022 Компьютерная газета