Статьи: Настройка библиотеки Flot для рисования сглаженных графиков

Задача

Для одно корпоративного сайта встала задача в рисовании графиков из заданного массива чисел. Нам требовалось решить следующие задачи:

  • необходимо видеть на одной картинке несколько графиков и возможность ставить точку;
  • предусмотреть сглаживание графиков;
  • возможность менять оформление графиков;
  • через JS получать название графика на котором была выбрана точка.

Для этой задачи мы выбрали бесплатное решение Flot.

Реализация

Начнем с простого, с HTML. Создадим файлик image-flot.php со следующим содержимым.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Flot Examples</title> <link href="layout.css" rel="stylesheet" type="text/css" /> <!--[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> <h1>Flot Examples</h1> <div id="placeholder" style="width:600px;height:300px;"></div> <p id="hoverdata">Мышка находится в позиции (<span id="x">0</span>, <span id="y">0</span>).<br /><span id="clickdata"></span></p> <p id="choices">Графики:</p> </body> </html>

image

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

  • блок "placeholder" используется для вывода графиков
  • блок "hoverdata" используется для вывода координат мышки
  • блок "clickdata" используется для вывода названия графика
  • блок "choices" используется для вывода всех графиков с возможностью их отключить или включить

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

<script id="source" language="javascript" type="text/javascript"> $(function () { var datasets = { "graph1": { label: "График номер один", data: [ <?php printAllPoint(array( 'x' => array(0,50,100,125,150,175,200,220), 'y' => array(245,209,160,135,100,87.5,30,0) )); ?> ] }, "graph2": { label: "График номер два", data: [ <?php printAllPoint(array( 'x' => array(0,50,100,125,150,175,200,260), 'y' => array(265,222,190,163,140,129,80,0) )); ?> ] }, "point": { label: "Точка", data: [ [50, 50] ], points: { show: true, fill: false } } }; }); </script>

Замечательно, видите, не все так сложно как кажется на первый взгляд. Идем дальше. Как вы видите я использую функцию printAllPoint() для заполнения координат x,y. На данном этапе она просто преобразует массив точек в понятный для Flot формат. Чуть дальше она нам будет нужна для сглаживания графиков.

function printAllPoint($arrPoints) { foreach($arrPoints['x'] as $key => $point) { print "[" . $point . "," . $arrPoints['y'][$key] . "],"; } }

Теперь нам нужно вывести данные графики в блоке "placeholder". Добавим следующий JS код в наш скрипт.

// insert checkboxes
var choiceContainer = $("#choices");
$.each(datasets, function(key, val) { choiceContainer.append('<br/><input type="checkbox" name="' + key + '" checked="checked" id="id' + key + '">' + '<label for="id' + key + '">' + val.label + '</label>'); }); choiceContainer.find("input").click(plotAccordingToChoices); function plotAccordingToChoices() { var data = []; choiceContainer.find("input:checked").each(function () { var key = $(this).attr("name"); if (key && datasets[key]) data.push(datasets[key]); }); if (data.length > 0) { $.plot($("#placeholder"), data, { yaxis: { min: 0 }, xaxis: { tickDecimals: 0 } }); } } plotAccordingToChoices();

Да, вот такой монстрообразный JS код, но давайте в нем немного разберемся. Часть, которая идет перед JS функцией plotAccordingToChoices() отвечает за вывод checkbox'ов (именно в этой части мы будем включать и отключать графики). А сама функция plotAccordingToChoices() отвечает за вывод массива "datasets" в блок "placeholder". Я думаю что детально расписывать работу этой части не стоит, т.к. те, кто хочет разобраться разбирутся сами, а простым людям достаточно сделать Ctrl+C Ctrl+V.

Теперь смело запускаем скрипт в браузере.

image

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

Во первых давайте перепишем PHP функцию printAllPoint(). Теперь она должна выглядеть так:

function printAllPoint($arr) { $arrPoints = Splines($arr); foreach($arrPoints as $key => $params) { print "[".$params['x'].",".$params['y']."],"; } }

Не трудно догадаться, что у нас появилась еще одна функция, которая занимается поиском дополнительных точек между заданными точками. Входной параметр $s отвечает за кривизну полученной функции.

function Splines($pts = array(), $s = 0.5) { $arrPoints = array(); $k = 0; for ($i = 0; $i < count($pts['x']) - 1; $i++) { $cntElements = count($pts['x']); $P1x = ($i < 1) ? $pts['x'][ 0 ] : $pts['x'][$i - 1]; $P1y = ($i < 1) ? $pts['y'][ 0 ] : $pts['y'][$i - 1]; $P2x = $pts['x'][$i]; $P2y = $pts['y'][$i]; $P3x = $pts['x'][$i + 1]; $P3y = $pts['y'][$i + 1]; $P4x = ($i < $cntElements - 2) ? $pts['x'][$i + 2] : $pts['x'][$cntElements - 1]; $P4y = ($i < $cntElements - 2) ? $pts['y'][$i + 2] : $pts['y'][$cntElements - 1]; for ($j = 1; $j < 100 + 1; $j++) { $t = $j * 0.01; $x = $s * ( -$t * $t * $t + 2 * $t * $t - $t) * $P1x + $s * ( -$t * $t * $t + $t * $t) * $P2x + (2 * $t * $t * $t - 3 * $t * $t + 1) * $P2x + $s * ($t * $t * $t - 2 * $t * $t + $t) * $P3x + ( -2 * $t * $t * $t + 3 * $t * $t) * $P3x + $s * ($t * $t * $t - $t * $t) * $P4x; $y = $s * ( -$t * $t * $t + 2 * $t * $t - $t) * $P1y + $s * ( -$t * $t * $t + $t * $t) * $P2y + (2 * $t * $t * $t - 3 * $t * $t + 1) * $P2y + $s * ($t * $t * $t - 2 * $t * $t + $t) * $P3y + ( -2 * $t * $t * $t + 3 * $t * $t) * $P3y + $s * ($t * $t * $t - $t * $t) * $P4y; if($x >= 0 and $y >= 0) { $arrPoints[$k]['x'] = round($x); $arrPoints[$k]['y'] = round($y); $k++; } } } return $arrPoints; }

Давайте сохраним и посмотрим, что у нас получилось.

image

Ну не красота ли. Хорошо, давайте теперь вернемся к поставленной задачи. А именно возможности выбирать график по его точке. У Flot есть встроенный механизм для этого. Добавим в JS код следующий кусочек.

// следим за координатами курсора на графике
$("#placeholder").bind("plothover", function (event, pos, item) { $("#x").text(pos.x.toFixed(2)); $("#y").text(pos.y.toFixed(2)); });

И немного изменим JS функцию plotAccordingToChoices();

function plotAccordingToChoices() { var data = []; choiceContainer.find("input:checked").each(function () { var key = $(this).attr("name"); if (key && datasets[key]) data.push(datasets[key]); }); if (data.length > 0) { $.plot($("#placeholder"), data, { yaxis: { min: 0 }, xaxis: { tickDecimals: 0 }, grid: { hoverable: true } }); } }

Запускаем! Повозив вдоволь мышкой на графику приступаем к дальнейшему изучению. Добавляем еще один кусочек JS кода.

// по клику показывает на каком именно графике стоит мышка $("#placeholder").bind("plotclick", function (event, pos, item) { if (item) { $("#clickdata").text("Вы щелкнули на " + item.dataIndex + " в " + item.series.label + "."); plot.highlight(item.series, item.datapoint); } });

И еще чуть-чуть меняем нашу JS функцию plotAccordingToChoices().

function plotAccordingToChoices() { var data = []; choiceContainer.find("input:checked").each(function () { var key = $(this).attr("name"); if (key && datasets[key]) data.push(datasets[key]); }); if (data.length > 0) { $.plot($("#placeholder"), data, { yaxis: { min: 0 }, xaxis: { tickDecimals: 0 }, grid: { hoverable: true, clickable: true } }); } }

Отлично, чувствуете, что вы владеете миром? Это очень хорошо, тогда запускаем наш скрипт.

image

Для тех кому не нравится врешний вид стандартного графика его можно легко поменять. Например вот так:

function plotAccordingToChoices() { var data = []; choiceContainer.find("input:checked").each(function () { var key = $(this).attr("name"); if (key && datasets[key]) data.push(datasets[key]); }); if (data.length > 0) { $.plot($("#placeholder"), data, { yaxis: { min: 0 }, xaxis: { tickDecimals: 0 }, grid: { hoverable: true, clickable: true, color: "#000", // text backgroundColor: "#fffeb4", // background yellow tickColor: "#cecece", // grip gray borderColor: "#000000" // border black } }); } }

Этот рассказ не претендует на полное описание возможностей библиотеки Flot. Но для базовый понятий мне кажется совсем не плохо. Спасибо вам за внимание.