D3 — это библиотека JavaScript, которую можно использовать для создания интерактивных диаграмм с технологией HTML5 Scalable Vector Graphics (SVG). Работать напрямую с SVG для создания диаграмм может быть болезненно, поскольку нужно помнить формы, поддерживаемые SVG, и сделать несколько вызовов API, чтобы сделать диаграмму динамичной. D3 устраняет большинство проблем и предоставляет простой интерфейс для построения графиков на основе SVG. Джей Радж опубликовал две замечательные статьи SitePoint о работе с D3, ознакомьтесь с ними, если вы еще не знакомы с D3.
Большинству из вас может не понадобиться официальное введение в AngularJS . AngularJS — это клиентский JavaScript-фреймворк для создания многофункциональных веб-приложений. Одним из главных преимуществ AngularJS является поддержка директив. Директивы предоставляют отличный способ определить наши собственные свойства и элементы HTML. Это также помогает хранить разметку и код отдельно друг от друга.
AngularJS также очень силен в связывании данных. Эта функция экономит много времени и усилий, необходимых для обновления пользовательского интерфейса в соответствии с данными в модели. В современном веб-мире клиенты просят разработчиков создавать веб-сайты, которые отвечают в режиме реального времени. Это означает, что клиенты хотят всегда видеть последние данные на экране. Пользовательский интерфейс данных должен обновляться, как только кто-то изменяет часть данных в серверной части. Выполнение таких обновлений в реальном времени будет очень трудным и неэффективным, если у нас не будет поддержки привязки данных.
В этой статье мы увидим, как создавать директивы AngularJS в реальном времени, которые обертывают диаграммы D3.
Настройка
Во-первых, нам нужно настроить среду. Нам нужны AngularJS и D3, включенные в HTML-страницу. Поскольку мы создадим только директиву диаграммы, нам нужно создать контроллер AngularJS и директиву. В контроллере нам нужна коллекция, содержащая данные, которые должны быть нанесены на график. Следующий фрагмент показывает начальный контроллер и директиву. Мы добавим больше кода к этим компонентам позже.
var app = angular.module("chartApp", []);
app.controller("SalesController", ["$scope", function($scope) {
$scope.salesData = [
{hour: 1,sales: 54},
{hour: 2,sales: 66},
{hour: 3,sales: 77},
{hour: 4,sales: 70},
{hour: 5,sales: 60},
{hour: 6,sales: 63},
{hour: 7,sales: 55},
{hour: 8,sales: 47},
{hour: 9,sales: 55},
{hour: 10,sales: 30}
];
}]);
app.directive("linearChart", function($window) {
return{
restrict: "EA",
template: "<svg width='850' height='200'></svg>",
link: function(scope, elem, attrs){
}
};
});
Мы заполним функцию связи в вышеприведенной директиве, чтобы использовать данные, хранящиеся в контроллере, и построим линейную диаграмму, используя D3. Шаблон директивы содержит элемент svg
Мы применим API D3 к этому элементу, чтобы построить график. Следующий фрагмент демонстрирует пример использования директивы:
<div linear-chart chart-data="salesData"></div>
Теперь давайте соберем основные данные, необходимые для построения графика. Он включает в себя данные для построения графика, объект JavaScript элемента SVG и другие статические данные.
var salesDataToPlot=scope[attrs.chartData];
var padding = 20;
var pathClass = "path";
var xScale, yScale, xAxisGen, yAxisGen, lineFun;
var d3 = $window.d3;
var rawSvg = elem.find("svg")[0];
var svg = d3.select(rawSvg);
После загрузки библиотеки для d3 объект d3
Но если мы используем его непосредственно внутри блока кода, этот блок кода будет сложно протестировать. Чтобы сделать директиву тестируемой, я использую объект через $ window .
Рисование простой линейной диаграммы
Давайте настроим параметры, необходимые для построения графика. Диаграмме нужны ось x, ось y и область данных, которые будут представлены этими осями. В этом примере ось X обозначает время в часах. Мы можем взять первое и последнее значения в массиве. На оси Y возможные значения находятся в диапазоне от нуля до максимального значения продаж. Максимальное значение продаж можно найти с помощью d3.max()
Диапазон осей варьируется в зависимости от высоты и ширины элемента svg
Используя приведенные выше значения, нам нужно попросить d3 нарисовать оси с желаемой ориентацией и количеством тиков. Наконец, нам нужно использовать d3.svg.line()
Все вышеперечисленные компоненты должны быть добавлены к элементу svg
Мы можем применять стили и преобразования к диаграмме при добавлении элементов. Следующий код устанавливает параметры и добавляет к SVG:
function setChartParameters(){
xScale = d3.scale.linear()
.domain([salesDataToPlot[0].hour, salesDataToPlot[salesDataToPlot.length - 1].hour])
.range([padding + 5, rawSvg.clientWidth - padding]);
yScale = d3.scale.linear()
.domain([0, d3.max(salesDataToPlot, function (d) {
return d.sales;
})])
.range([rawSvg.clientHeight - padding, 0]);
xAxisGen = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(salesDataToPlot.length - 1);
yAxisGen = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(5);
lineFun = d3.svg.line()
.x(function (d) {
return xScale(d.hour);
})
.y(function (d) {
return yScale(d.sales);
})
.interpolate("basis");
}
function drawLineChart() {
setChartParameters();
svg.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0,180)")
.call(xAxisGen);
svg.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(20,0)")
.call(yAxisGen);
svg.append("svg:path")
.attr({
d: lineFun(salesDataToPlot),
"stroke": "blue",
"stroke-width": 2,
"fill": "none",
"class": pathClass
});
}
drawLineChart();
Вот демо, показывающее график выше.
Обновление графика в реальном времени
Как указывалось ранее, благодаря сегодняшним возможностям Интернета наши пользователи хотят, чтобы диаграммы данных обновлялись немедленно по мере изменения базовых данных. Измененная информация может быть передана клиенту с использованием таких технологий, как WebSockets. Директива диаграммы, которую мы только что создали, должна быть в состоянии реагировать на такие изменения и обновлять диаграмму.
Для передачи данных через WebSockets нам нужен компонент на сервере, созданный с использованием Socket.IO с Node.js, SignalR с .NET или аналогичная технология на других платформах. Для демонстрации я использовал $interval
$interval(function() {
var hour = $scope.salesData.length + 1;
var sales = Math.round(Math.random() * 100);
$scope.salesData.push({hour: hour, sales: sales});
}, 1000, 10);
Чтобы обновить диаграмму, как только будут загружены новые данные, нам нужно перерисовать диаграмму с обновленными данными. Наблюдатель сбора должен использоваться в директиве для отслеживания изменений в данных сбора. Наблюдатель вызывается, когда в коллекцию вносятся какие-либо изменения. График перерисовывается в наблюдателе.
scope.$watchCollection(exp, function(newVal, oldVal) {
salesDataToPlot = newVal;
redrawLineChart();
});
function redrawLineChart() {
setChartParameters();
svg.selectAll("g.y.axis").call(yAxisGen);
svg.selectAll("g.x.axis").call(xAxisGen);
svg.selectAll("." + pathClass)
.attr({
d: lineFun(salesDataToPlot)
});
}
Полное демо можно найти здесь .
Вывод
AngularJS и D3 — очень полезные библиотеки для создания многофункциональных бизнес-приложений в Интернете. Мы обсудили, как использовать их вместе, чтобы создать простую диаграмму. Вы можете расширить эти знания для создания диаграмм для ваших приложений.