Графики и диаграммы на веб-страницах. Часть 2
В прошлой статье я показал заготовку диаграммы: создал несколько массивов с числовыми данными (серии данных) и рассказал о том, как настроить диаграмму с данными, расположенными вдоль временной шкалы. Сейчас я покажу то, как создать диаграмму с двумя осями 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 '<div style="border: 1px solid red;">'+value+'</div>';
}
В том, случае, если количество линий-отметок не велико, то можно использовать прием с явным перечислением списка значений оси OX, при которых нужно ставить отметку. И каждому значению tick-а можно поставить в соответствии текст надписи, который выводится рядом с линей tick-а. В следующем примере я решил вывести три точки отметки –Pi/4, затем 0 и +Pi/4. Но в отличие от предыдущего примера, где количество радиан перед выводом на экран просто округлялось до тысячи, теперь мне хочется вывести подписи, содержащие изображение числа (Unicode код символа pi равен ‘\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 '<i>'+value+'</i>';
}
var chartConfig = {
legend: {
show: true, position: "sw", backgroundColor: "#00ff00",
backgroundOpacity: 0.5, labelBoxBorderColor: "#caca00", labelFormatter: formatLabel
}
}
Иногда возникает потребность в размещении легенды диаграммы не внутри диаграммы, а где-нибудь в другом месте html-страницы, например, под диаграммой. Или содержимое страницы представляется в виде таблицы из двух колонок. Так, первая колонка будет содержать диаграмму, а вторая ее легенду. В любом случае, разработчики flot предусмотрели способ создать html-блок “заглушку” для последующего размещения в ней легенды диаграммы:
<div id="legend" style="position: absolute; left: 900px; top: 800px; border: 1px solid red;">
Это заглушка для легенды
</div>
А при настройке внешнего вида диаграммы, я могу подсказать 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) {
$('<div id="tooltip">' + contents + '</div>').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.