Использование AngularJS для визуализации данных

Перевод статьи Using AngularJS for Data Visualisations с сайта css-tricks.com, автор — Ник Мортон.

Перед вами гостевой пост от Ника Мортона. Должен сказать, что я весьма заинтригован этой идеей. Знаю, что я люблю работать с HTML, SVG и CSS, поэтому, когда Ник поделился со мной мыслью, что мы можем использовать это для создания структуры, и использовать данные непосредственно для стилизации графиков, я заинтересовался. Кроме того, понимание, что благодаря использованию Angular график может автоматически изменяться при изменении данных… это чертовски круто.

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

В этом уроке я расскажу о создании трех различных видов графиков с помощью встроенных CSS и SVG.

Почему Angular?

Если вам когда-либо приходилось добавлять элементы в DOM с помощью JavaScript или jQuery, то вы знаете, как быстро ваш код может превратиться в кашу. Особенно, если вы используете несколько переменных. Angular позволяет использовать данные прямо в разметке, что ведет к чистому и простому для чтения и восприятия коду.

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

Я не утверждаю, что это самый лучший способ для создания визуализаций данных, но он точно мне по душе!

Примечание: в этой статье я не буду рассматривать все тонкости Angular, такие как работа приложений, контроллеров и т.д. – я буду сконцентрирован на данных. Я написал приложение 'Hello World на AngularJS', которое может помочь вам разобраться с некоторыми из этих вещей. Кроме того, существует множество великолепных ресурсов, которые помогут вам начать работать с Angular.

Настройка нашего приложения на Angular

Базовая настройка приложения

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

(function(){

  var app = angular.module('graphApp',[]);

  app.controller('graphController', function($scope){

  // Здесь будет код!

  });

})();

Настройки по умолчанию

После этого мы установим значения нескольких переменных, используемых по умолчанию, которые привязаны к контексту ($scope) контроллера. Их мы будем использовать для управления размером нашего графика, а также метками осей X и Y.

$scope.width = 600;
$scope.height = 400;
$scope.yAxis = "Sales";
$scope.xAxis = "2014"

Данные

После этого мы добавим свои данные, записанные в формате JSON, и привяжем их к  $scope нашего контроллера.

$scope.data = [
  {
    label: 'January',
    value: 36
  },
  {
    label: 'February',
    value: 54
  },

  // .... и так далее .....

  {
    label: 'November',
    value: 252
  },
  {
    label: 'December',
    value: 342
  }
];

Поиск максимального значения

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

$scope.max = 0;

var arrLength = $scope.data.length;
for (var i = 0; i < arrLength; i++) {
  // Найти максимальное значение на оси X
  if ($scope.data[i].value > $scope.max)
  $scope.max = $scope.data[i].value;
}

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

Настройка разметки, шаблонов и CSS

Теперь нам нужно настроить разметку и CSS нашего приложения для визуализации.

<div ng-app="graphApp">

  <div ng-controller="graphController as graph">

    <div class="graph" style="width:{{width}}px; height:{{height}}px;" >

      <div class="y" style="width:{{height}}px;">{{yAxis}}</div>

      <div class="x">{{xAxis}}</div>

    </div>

  </div>

</div>

Внутри HTML мы можем использовать данные из JavaScript прямо в разметке, как в качестве контента (например, метки осей X и Y), так и во встроенных стилях для управления высотой и шириной нашего графика.

Примечание: div 'y' использует переменную {{height}} для CSS-свойства width  — это связано с тем, что в CSS мы повернем эту ось на 90 градусов против часовой стрелки.

.chart {
  border-left: 1px solid black;
  border-bottom: 1px solid black;
  margin: 60px auto;
  position: relative;
}

.y {
  position: absolute;
  transform: rotate(-90deg);
  transform-origin: bottom left;
  bottom: 0;
  padding: 5px;
}

.x {
  position: absolute;
  top: 100%;
  width: 100%;
  padding: 5px;
}

Гистограмма

Окей, для создания данных в наших таблицах мы будем использовать функцию Angular ng-repeat. Она циклически пройдет по нашему массиву данных и для каждой записи выдаст ту разметку, в которую мы ее обернем.

Сперва мы создадим гистограмму. Для начала я задал для класса bar базовый стиль CSS, который устанавливает значение position: absolute и добавляет цвет для заднего фона.

.bar {
  background: blue;
  position: absolute;
  bottom: 0;
}

Затем, с помощью ng-repeat создадим <div> с классом 'bar' для каждой записи, которую мы будем выводить

<div ng-repeat="bar in data" class="bar"></div>

Этот код следует поместить внутрь нашего <divclass="graph"></div>.

Теперь мы можем использовать наши данные Angular (и немного математики!) в некоторых встроенных стилях CSS для управления высотой и шириной каждого столбца.

<div ng-repeat="bar in data" class="bar" style="height:{{bar.value / max * height}}px; width:{{width / data.length - 5}}px;"></div>

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

Для расчета ширины мы разделим всю ширину на количество записей, добавив отступ в 5px, чтобы оставить немного места при размещении наших столбцов на оси X.

Наконец, нам нужно расположить столбцы вдоль оси X с помощью свойства CSS left

<div ng-repeat="bar in data" class="bar" style="height:{{bar.value / max * height}}px; width:{{width / data.length - 5}}px; left:{{$index / data.length * width}}px;"></div>

В этом примере мы используем $index, переменную Angular, значения которой начинаются с 0 и увеличиваются для каждого последующего столбца. Мы делим значение индекса на общее количество записей и умножаем его на значение всей ширины графика. Таким образом первый столбец будет помещен в 0, а остальные будут равномерно распределены по всему графику.

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

Вот и все – наша гистограмма готова, и теперь вы можете немного поиграться с CSS, чтобы ее приукрасить!

Полный код для создания графика выглядит вот так:

<div class="chart" style="width:{{width}}px; height:{{height}}px;">

  <!-- Метки -->
  <div class="y" style="width:{{height}}px;">{{yAxis}}</div>
  <div class="x">{{xAxis}}</div>

  <!-- Данные -->
  <div ng-repeat="bar in data" class="bar" style="height:{{bar.value / max * height}}px; width:{{width / data.length - 5}}px; left:{{$index / data.length * width}}px;"></div>

</div>

Точечная диаграмма

Для создания точечной диаграммы используется очень похожий метод.

И снова я задаю для точек базовые стили CSS:

.dot {
  background: blue;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  position: absolute;
}

Работая с точками, нам не нужно переживать о высоте и ширине. Мы просто используем те же самые данные и математику для размещения точек, устанавливая отступы слева и снизу.

Единственная разница заключается в том, что мы добавляем к $index 0.5, чтобы точки располагались точно в центре выделяемого для них пространства.

<div ng-repeat="dot in data" class="dot" style="bottom:{{dot.value / max * height}}px; left:{{($index + 1) / data.length * width}}px;"></div>

Полный код для графика:

<div class="chart" style="width:{{width}}px; height:{{height}}px;">

  <!-- Метки -->
  <div class="y" style="width:{{height}}px;">{{yAxis}}</div>
  <div class="x">{{xAxis}}</div>

  <!-- Данные -->
  <div ng-repeat="dot in data" class="dot" style="bottom:{{dot.value / max * height}}px; left:{{($index + 0.5) / data.length * width}}px;"></div>

</div>

Как и в случае со встроенными стилями CSS, мы можем использовать значения данных Angular в данных SVG.

Для этого мы используем следующий код CSS:

svg {
  position: absolute;
  transform: rotateX(180deg);
  left: 0;    
}

line  {
  stroke:red;
  stroke-width:3px;
}

Я поворачиваю SVG, поскольку по умолчанию оно обрабатывает значения сверху, а нам нужно развернуть его на 180 градусов, чтобы брать значения снизу.

Сперва нам нужно создать SVG, который будет занимать всю площадь нашего графика. После этого внутри мы используем ng-repeat на элементе line.

Каждый элемент line требует начальной и конечной точки на осях X (x1, x2) и Y (y1, y2).

С осью X все довольно просто – мы действуем по той же схеме, что и раньше, чтобы все линии на графике располагались равномерно по всей его ширине, начиная с 0, используя $index, а заканчивались там, где начинается следующая линия, используя $index + 1.

На оси Y переменная $index используется сама по себе, позволяя нам выбирать значения или предыдущих или следующих записей в нашем массиве.

Исходная точка Y каждой из линий получает значение из предыдущей записи данных с помощью data[$index - 1].value, после чего мы используем расчеты, подобные использованным в предыдущих примерах. Для второй точки Y мы можем вызвать значение прямо из записи.

Это может показаться сложным (и я уверяю вас, что для того, чтобы дойти до этого, мне пришлось пораскинуть мозгами!), но я надеюсь, что мое объяснение вместе с представленным ниже кодом помогут вам разобраться, что к чему!

<svg style="width:{{width}}px; height:{{height}}px;">

  <line ng-repeat="line in data" x1="{{$index / data.length * width}}" y1="{{data[$index - 1].value / max * height}}" x2="{{($index + 1) / data.length * width}}" y2="{{line.value / max * height}}">
  </line>

</svg>

Окончательный код для графика выглядит следующим образом:

<div class="chart" style="width:{{width}}px; height:{{height}}px;">

  <!-- Метки -->
  <div class="y" style="width:{{height}}px;">{{yAxis}}</div>
  <div class="x">{{xAxis}}</div>

  <!-- Данные -->
  <svg style="width:{{width}}px; height:{{height}}px;">

    <line ng-repeat="line in data" 
          x1="{{$index / data.length * width }}" 
          y1="{{data[$index - 1].value / max * height}}" 
          x2="{{($index + 1) / data.length * width}}" 
          y2="{{line.value / max * height}}">
    </line>

 </svg>

</div>

Что дальше?

Вы можете использовать переменную $index и в именах классов, вот так <elementclass="classname{{$index}}"> , что позволит производить более тонкую настройку каждого из столбцов, точек или линий – что может пригодиться для анимации этих визуализаций.

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

Кроме того, я сделал проект на  Codepen со всеми этими примерами.

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

 

P.S. Это тоже может быть интересно:

2 Комментарии

  1. Петро

    Это конечно хорошо, понимать как можно сварганить диаграмку. Но для более серьезных вещей все же стоит использовать что-то типа D3.js, дабы не плодить столько кода

  2. nerv

    1. если посмотреть в консоль на данной странице, то будет ад =)

    2. код раздела "Базовая настройка приложения" можно переписать так

    [code]angular
        .module('graphApp',[])
        .controller('graphController', function($scope){

            // Здесь будет код!
        });[/code]

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.