Flot — рисуем графики в jQuery

Flot — это JavaScript библиотека для jQuery, предназначенная для рисования графиков. С ее помощью можно легко и быстро создавать красивые динамические графики, которые будут корректно работать в любом браузере независимо от того, какая ОС установлена у пользователя.

Примечание: все примеры, библиотека Flot, а также кое-какие дополнительные материалы к этой заметке собраны в этом архиве.

1. Простой график

Давайте нарисуем с помощью Flot какой-нибудь несложный график, например синусоиду. Чтобы было чуточку интересней, пусть графиков будет сразу несколько. Для передачи данных библиотеке Flot, их нужно представить в следующем виде:

var all_data = [
  { label: "Данные 1", color: 0, data: [[1, 0], [2, 10], [3, 70]]},
  { label: "Данные 2", color: 1, data: [[1, 130], [2, 230], [3, 320]]}
  // ВАЖНО: IE не понимает запятых на конце :(
];

Числа 1, 2 и 3 — это координаты точек по оси абсцисс, числа рядом с ними — координаты по оси ординат. Указывать названия рядов и используемые цвета не обязательно. Будьте внимательны — если поставить запятую вслед за последним элементом массива или хэша, Internet Explorer скажет, что в коде ошибка, и откажется его выполнять. Все остальные браузеры обрабатывают эту ситуацию нормально, что в очередной раз доказывает, что создателям IE нужно оторвать руки.

Если мы хотим, чтобы по оси OX отображались даты и время, их значения должно быть представлено в UTC (кто с ним не знаком — это числа типа 1149537600000). Мне кажется не очень удобным работать с UTC, тем более, что избежать этого можно с помощью незамысловатого приема:

var all_data = [
  { label: "Данные 1", color: 0, data: [["2010/10/01", 0], ["2010/11/01", 1], ["2010/12/01", 7]]},
  { label: "Данные 2", color: 1, data: [["2010/10/01", 13], ["2010/11/01", 23], ["2010/12/01", 32]]}
];
// преобразуем даты в UTC
for(var j = 0; j < all_data.length; ++j) {
  for(var i = 0; i < all_data[j].data.length; ++i)
    all_data[j].data[i][0] = Date.parse(all_data[j].data[i][0]);
}

Можно считать, что все уже написано — до готового графика осталось совсем чуть-чуть:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Название страницы</title>
  <!-- подгружаем Flot -->
  <!--[if IE]><script language="javascript" type="text/javascript" src="flot/excanvas.min.js"></script><![endif]-->
  <script language="javascript" type="text/javascript" src="flot/jquery.js"></script>
  <script language="javascript" type="text/javascript" src="flot/jquery.flot.js"></script>
</head>
<body>
  <p><noscript><strong style="color: red;">Для отображения данных необходимо включить JavaScript!</strong></noscript></p>
  <!-- тут будет выводится график -->
  <div id="placeholder" style="width:600px;height:300px;"></div>

<script language="javascript" type="text/javascript">
// данные для графиков
var all_data = [
  { data: [["2010/10/01", 0], ["2010/10/5", 1], ["2010/10/10", 7], ["2010/10/15", 8]]},
  { data: [["2010/10/01", 13], ["2010/10/5", 23], ["2010/10/10", 32], ["2010/10/15", 33]]}
];
// преобразуем даты в UTC
for(var j = 0; j < all_data.length; ++j) {
 for(var i = 0; i < all_data[j].data.length; ++i)
   all_data[j].data[i][0] = Date.parse(all_data[j].data[i][0]);
}
// свойства графика
var plot_conf = {
 series: {
   lines: {
     show: true,
     lineWidth: 2
   }
 },
 xaxis: {
   mode: "time",
   timeformat: "%y/%m/%d",
 }
};
// выводим график
$.plot($("#placeholder"), all_data, plot_conf);
</script>
</body>
</html>

На мой взгляд, все предельно просто и в комментариях не нуждается. Результат выглядит следующим образом:

Простой график в Flot

2. Выводим легенду

Вывести легенду к графику очень просто — достаточно указать label в описании данных, как мы это делали в начале статьи. Как вы могли заметить, в последнем примере ассоциативный массив содержал только элементы data.

По умолчанию легенда отображается в правом верхнем углу графика, частично закрывая его. Чтобы легенда отобразилась в конкретном месте на странице, достаточно дописать в переменную plot_conf

  legend: {
    container: $("#legend")
  } // помним про запятые и IE

где legend — это id слоя, в котором будет выведена легенда. Полный код примера вы можете посмотреть в файле step2.html, который находится в прилагающемся к статье архиву.

3. Скрываем данные

С помощью Flot можно сделать одну очень удобную штуку — добавить в легенду чекбоксы, кликая по которым пользователь сможет скрывать ряды данных. К сожалению, в Flot нет метода типа СкрытьРяд(), потому при каждом клике график придется полностью перерисовывать. А давайте-ка в честь этого напишем функцию, отвечающую за перерисовку!

// рисуем графики в первый раз
redraw();

function redraw() {
  var data = [];
  for(var j = 0; j < all_data.length; ++j)
    if(!hide[j]) // что скрываем, а что нет
      data.push(all_data[j]);

  $.plot($("#placeholder"), data, plot_conf);
 
  // легенду рисуем только один раз
  plot_conf.legend.show = false;
}

Массив hide содержит all_data.length элементов. Если i-ый элемент массива равен true, значит i-ый ряд данных нужно скрыть. После первого рисования графика следует присвоить plot_conf.legend.show значение false. Легенду достаточно нарисовать один раз, незачем тратить на ее повторное создание лишние такты процессора.

После того, как Flot нарисует легенду, мы можем добавить в нее чекбоксы:

// рисуем чекбоксы в легенде
var legend = document.getElementById('legend'); // еще IE не умеет заменять innerHTML в table
var legend_tbl = legend.getElementsByTagName('table')[0];
var legend_html = '<table style="font-size: smaller; color: rgb(84, 84, 84);"><tbody>';
for(var i = 0; i < legend_tbl.rows.length; i++) {
  legend_html += '<tr>' +
    '<td><input type="checkbox" onclick="hide['+ i +']=!hide['+ i +'];redraw();" checked="1"></td>'
    + legend_tbl.rows[i].innerHTML
    + '</tr>';
}
legend_html += "</tbody></table>";
legend.innerHTML = legend_html;

Internet Explorer не умеет изменять innerHTML таблиц, в связи с чем приходится переписывать таблицу с нуля и присваивать полученный код innerHTML слою, эту таблицу содержащему. Интересное объяснение того, почему IE так себя ведет, вы найдете на ХабраХабре. На момент написания этих строк, статья была недоступна:

Доступ к публикации закрыт

Вы пытаетесь открыть публикацию, написанную пользователем GreLI.
Автор переместил топик в черновики.

На всякий случай я взял копию статьи из кэша Google и сохранил в архиве, прилагающемуся к статье.

При использовании приема с чекбоксами важно не забыть жестко прописать соответствие нашим данным конкретных цветов, иначе при перерисовке графика ряды будут менять свой цвет. В результате описанных в этом разделе действий мы получим что-то вроде этого:

Flot: график с легендой

Полный код примера вы найдете в файле step3.html.

4. Кроме того

Возможности Flot настолько велики, что для полного их описания потребовалась бы далеко не одна и не две таких статьи. За кадром осталась поддержка плагинов, подгрузка данных с помощью Ajax и многое другое. Чтобы составить более-менее полное впечатление о возможностях библиотеки, загляните в раздел с примерами на сайте проекта.

Напоследок я приведу еще один пример, работу которого я хотел бы оставить для самостоятельного изучения.

Сложный пример использования Flot

Нажимая по ссылке «Сменить вид», пользователь может выбирать, в каком виде будут отображаться данные — в виде гистограмм или графиков. Превью внизу страницы позволяет выбрать отображаемый диапазон данных. Полный код примера вы найдете в прилагающемся к статье архиву, в файле result.html.

5. Заключение

Flot — это прекрасная библиотека, которая должна быть в арсенале каждого веб-разработчика. Открытый код, мощный функционал, полная кроссплатформенность, отличная документация — о чем еще можно мечтать? В очередной раз я убеждаюсь в том, что этот ваш Flash никому не нужен!

Дополнение: Если у вас возникли проблемы с работой Flot в Internet Explorer 9, загляните сюда.

Подпишитесь на блог с помощью RSS, E-Mail, Google+ или Twitter!
Также, пользуясь случаем, приглашаю вас посетить мой форум.

  • Al

    Перемести эту фигню с левой стороны экрана в правую часть пожалуйста. Читать невозможно, раздражает очень сильно. Спасибо.

  • http://eax.me/ Безумный Программист

    Теперь лучше? )

  • Al

    Все равно немного раздражает, но намного лучше, спасибо :)

  • Mactersait

    Как данные из базы брать?
    И по этим данным строит график

    • http://eax.me/ Безумный Программист

      Тут есть пример работы с БД: http://eax.me/graphviz/

  • http://www.facebook.com/people/Sumasedsij-Slon/1787147427 Сумашедший Слон

    Отличная статья, подписался на новые статьи)

  • Wladuha

    Спасибо за отличный материал.
    Есть вопрос, как правильно добавить время для вывода в первом примере, если формат даты вместо «2010/10/01″ пишу «2010/10/01T06:00:00″ и вывод по оси Х ставлю
    xaxis: {
    mode: «time»,
    timeformat: «%y/%m/%d %H:%M:%S»
    }
    то получаю на шкале вместо 2010/10/01 06:00:00 2010/09/30 23:00:00, т.е. смешение на 7 часов назад. Может я что-то не так делаю, подскажите пожалуйста

    • http://eax.me/ Безумный Программист

      Трудно сказать, я с временем не работал — только с датами. Могут влиять часовые пояса. Например, вы могли указать время со сдвигом + сказались настройки часового пояса у клиента. Если проблема не разрешится, адресуйте вопрос на мой форум — там более подходящее место для обсуждения.

  • Mike

    недавно написал подобный плагин, графики строятся на HTML5 если кому то интересно вот ссылка graph.prootime.ru

    • http://eax.me/ Безумный Программист

      Неплохо. А что не нравилось в существующих решениях, что решили написать свое?

  • manking

    В опциях
    series: — > lines:
    есть знчаение fill закрасить
    fill: true

    Кто нибудь знает, можно ли сделать, что если линия уходит в отрицательные значения то она закрашивается другим цветом?

  • Andreyscov

    К примеру из базы выводится только 3 значения, а по оси х даты выводятся беспорядочно,
    если, к примеру всего 3 значения, то даты выводятся так Окт4 Окт4 Окт4 Окт5 Окт5 Окт5 Окт6 Окт6
    Как убрать дубли ?

    • http://eax.me/ afiskon

      Эм… использовать distinct? Не уверен, что понял вопрос. Вы бы лучше на форуме его задали.

      • Andreyscov

        Не база здесь не причем, просто ось Х заполняется вся ? а надо если 2 или 3 значения то линия графика строилась по 2 точкам и не растягивалась на 100%.
        К примеру http://people.iola.dk/olau/flot/examples/basic.html

        • http://eax.me/ afiskon

          А, ну то есть вам нужно, чтобы по оси OX был нужный вам интервал независимо от числа выводимых точек? Типа, чтобы график не «масштабировался»? Сделать это можно, но вот только как — вам лучше ответит документация, чем я. С Flot я довольно давно уже не работаю.

  • http://sepetov.ru/ Денис Сепетов

    Александр, ты просто находка для меня. В очередной раз хочу сказать спасибо :)

    Ну вот казалось бы — чё я могу найти на твоём блоге, чтобы это пригодилось мне в ФИЗИКЕ?! Однако уже второй случается — за графики спасибо. Теперь буду это себе в программу прикручивать.

    P. S. Почитаю дальше твои посты. Может чего его накопаю :D

  • Dinamo0

    а как сделать вспывающие подсказки, только чтобы значение временной шкалы отображались в челочеческом выгляде? Вот в таком виде время в UTC тображается
    $(«#placeholder»).bind(«plothover»,
    function (event, pos, item) {
    $(«#tooltip»).remove();
    if (! item) return;

    var x = item.datapoint[0].toFixed(0);
    var y = item.datapoint[1].toFixed(2);
    var label = y +» UAH» + » çà »+item.series.label+ «» +x;
    showTooltip(item.pageX, item.pageY,label);
    }
    );

    function showTooltip(x, y, contents) { $(» + contents + »).css( { position: ‘absolute’,
    display: ‘none’, top: y + 5, left: x + 5, border: ’1px solid #fdd’, ‘border-radius’: ’10px’,
    padding: ’2px’, ‘background-color’: ‘#fee’, opacity: 0.80 }).appendTo(«body»).fadeIn(200);}

    • http://eax.me/ afiskon

      Честно говоря, ни по Flot’у, ни по JavaScript, ни тем более по всплывающим подсказкам я уже давно ничего не ведаю. Попробуйте спросить на http://it-talk.org/ Если создадите топик на форуме, выложите пожалуйста сразу полный демонстрационный код страницы, чтобы можно было легко посмотреть код в действии и поправить его.