Highstock: мониторим Премию Рунета

    Долгое время в своих проектах использовал Flash-графики, вместо обычных картинок. Они и красивее и более функциональны, да и серверу не нужно напрягаться для генерации картинок. Но в связи с тем, что некоторые фирмы не очень дружат с Flash, пришлось поискать замену без использования Flash.

    До этого использовал AmCharts, первым делом глянул на их сайт, они уже выпустили JS версию своих графиков, но как-то не понравилось, по ощущениям — сыровато еще. После недолгого гугления был найден Highstock, от авторов Highcharts JS. Отличается в первую очередь возможностью выводить графики, привязанные к времени, с большим количеством точек, масштабированием, удобной навигацией. Да и чисто внешне понравился больше AmCharts.

    Сбор данных


    Для тренировки, чтобы было не сильно скучно, решил сделать простенькую страницу с мониторингом результатов народного голосования Премии Рунета. Так можно и с графиком попрактиковаться, да и посмотреть, будет ли аномальное поведение на голосовании (обычно говорящее о накрутках).

    Подготовку особо расписывать не буду, в общих чертах. Так как к самой премии не имею никакого отношения, поэтому просто написал PHP-скрипт, который, скачивает главную страницу, парсит её регулярками, после чего количество голосов и позиции заносит в базу. Скрипт повешен на крон и выполняется каждые 5 минут.

    Подготовка данных


    Итак, данные есть, теперь нужно подумать, как их выводить. Поскольку сайтов принимающих участие в голосовании 50 штук, плюс разрывы между сайтами составляют 2 порядка, то выводить все сайты вместе не имеет смысла. Немного пораскинув мозгами, остановился на следующих вариантах вывода:
    1. По умолчанию, выводятся первые 3 позиции.
    2. Диапазоны по 10 позиций (1-10,11-20 и т.п.).
    3. Выбор конкретных сайтов (1,3,10).
    4. Если выбран только один сайт, то показываются также его ближайшие конкуренты, один на позицию выше, другой на позицию ниже.
    Дальше нужно определиться с форматом генерируемых данных. В Highstock понимает 3 варианта.
    1. Простой массив со значениями для оси Y, в таких случаях значения по X рассчитываются автоматически в соответствии с указанными правилами.
      data: [0, 5, 3, 5]
    2. Массив, содержащий значения X,Y.
      data: [[5, 2], [6, 3], [8, 2]]
    3. Массив объектов — самый продвинутый вариант, позволяющий хоть каждую точку настраивать.
      data: [{
      	name: 'Точка 1',
      	color: '#00FF00',
      	y: 0
      }, {
      	name: 'Точка 2',
      	color: '#FF00FF',
      	y: 5
      }]

    Я выбрал второй вариант, так как первый не подходил из-за того что премия периодически в дауне находится и в графике есть «дырки», а в третьем избыточный функционал, который не нужен в данном случае.

    Также поскольку нужно выводить несколько графиков, решил избавиться от дублирования времени. В итоге PHP-скрипт генерит данные в формате JSON:
    data: [
        [1, id1val, id2val,…,idNval], // Первая точка
        [2, id1val, id2val,…,idNval], // Вторая точка
    …
    ]

    Где первое значение означает время (timestamp делится на 300 сек. и отнимается стартовое время). Для преобразования данных в отдельные массивы для каждого графика, напишем функцию
    
    // Преобразуем в отдельные массивы
    var c = info.data.length; // Количество графиков
    for(var i = 0, l = info.title.length; i < l; i++){
        seriesOptions[i] = {
            name: info.title[i], // Название сайта
            data: [] // Пустой массив с данными
        };
        for(var j = 0; j < c; j++){
    	// Добавляем пары timestamp в миллисекундах и количество голосов
            seriesOptions[i].data.push([(info.data[j][0] + 4404683) * 300000, info.data[j][i+1]]); 
        }
    }

    Настраиваем внешний вид графика


    Для начала переведем выводящиеся надписи на русский, изменим разделитель тысяч, и сделаем, чтобы время выводилось не в UTC, а в локальном времени пользователя.
    Highcharts.setOptions({
        lang: {
            rangeSelectorZoom: 'Маcштаб',
            rangeSelectorFrom: 'От',
            rangeSelectorTo: 'До',
            thousandsSep: ' '
        },
        global: {
            useUTC: false
        }
    });

    Далее настраиваем внешний вид с помощь объекта опций. Сначала общие опции.
    
    chart: {
        renderTo: 'chart', // DIV контейнер в который будет встраиваться график
        zoomType: 'x', // разрешаем зумировать по оси X 
        backgroundColor: '#f9f9f9' // Сделаем слегка серый фон
    }

    Настройка кнопок выбора временных интервалов.
    
    rangeSelector: {
        buttons: [{ // Настраиваем свои кнопки для временных интервалов
            type: 'hour', // Тип интервала: 'millisecond', 'second', 'minute', 'day', 'week', 'month', 'ytd' (текущий год), 'year' and 'all'
            count: 3, // Количество единиц указанных в type
            text: '3 часа'
        }, {
            type: 'all',
            text: 'всё'
        }],
        inputDateFormat: '%d.%m.%Y', // Меняем на привычный для нас формат даты в интервалах
        inputEditDateFormat: '%d.%m.%Y',
        buttonTheme: {
            width: 60 // Увеличим ширину кнопки
        },
        selected: 2 // Какая кнопка выбрана по умолчанию
    }

    Теперь займемся всплывающей подсказкой (выводится при наведении на график)
     
    tooltip: {
        backgroundColor: 'rgba(250, 250, 250, .85)', // Фон немного темнее
        borderColor: 'rgba(100, 100, 100, .90)', // Цвет границы (по умолчанию меняется автоматом)
        xDateFormat: '%d.%m.%Y %H:%M', // Наш формат даты
        // Тут немного увеличиваем размер даты
        headerFormat: '<span style="font-size: 12px">{point.key}</span><br/>',
        // Формат надписей в подсказке, названия цветом графика, а значения жирным
        pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b><br/>'
    }

    Включаем и настраиваем легенду (по умолчанию она выключена). Вывел наверх, так как вывод
    под графиком пока работает коряво (накладывается на шкалу времени), как и вывод сбоку (не правильно обрезаются длинные названия)
      
    legend: {
    	enabled: true,
    	align: 'center',
    	itemWidth:234, // указал ширину, чтобы выводились сайты в 4 колонки
    	verticalAlign: 'top'
    }


    Результат


    После того как всё собрали в кучу, добавляем для удобства кнопки выбора сайтов. И наблюдаем за «битвой титанов».

    Вот так это выглядит:

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

    И напоследок несколько ссылок:
    Официальный сайт Highcharts и Highstock
    Дока по Highstock API
    Официальный сайт amСharts
    Share post

    Comments 12

      +3
      Да, Highcharts классная штука! Недавно использовал в проекте на yii с помощью этой обертки
        0
        Да очень приятная библиотека.
        Кстати наверное не все знают, что можно включать/выключать отображение графика определенного сайта кликнув на его названии в легенде. Для увеличения нужного участка графика, нажать кнопку мышки и удерживая её выделить нужный участок, после чего отпустить кнопку.
        +1
        Я смотрю народ вообще не заморачивается на тему соблюдения правил. Интересно как отреагируют на это организаторы.
          –1
          Организаторы там тоже никогда не заморачивались насчет правил. :)
          0
          Помнится раньше были проблемы с 7-8 IE, вы не знаете, они их пофиксили?
            0
            У меня только IE9, пробовал в режиме совместимости под IE7-8 — график нормально работает.
            Пробовал также под Android, на Opera Mobile график нормально работает, а вот в Dolphin Browser HD почему не захотел.
              +4
              Эта фраза применима в любое время к любому проекту.
                0
                Я имел ввиду серьезные проблемы :) Какие-то мелкие кроссбраузерные закорючки это дело такое, они все легко допиливаются напильником.
                0
                для старых осликов (IE) была надстройка Google Chrome Frame для корректного отображения HTML5
                0
                Ну и прайсинг у них не хилый. Так что для коммерческого использования придется раскошелиться…
                  0
                  Включаем и настраиваем легенду (по умолчанию она выключена). Вывел наверх, так как вывод под графиком пока работает коряво

                  Поиграйтесь со значением «spacingBottom» в свойстве «chart», выставите его в 40 например, должно помочь, про это был вопрос на форуме хайчарта, ссылку сейчас не найду.

                  А графики глючные, даже очень, но пока это, кажется, лучшее решение на джаваскрипте.
                    +1
                    Вся сложность в том, что неизвестно какой именно размер будет у легенды, так как может выводиться как 2-3 сайта, так и пару десятков. Когда легенда вверху находится, то отступ сам считается нормально. А когда внизу, то похоже еще не сделали, чтобы таймлайн учитывалась.

                  Only users with full accounts can post comments. Log in, please.