« Графики и диаграммы на веб-страницах. Часть 3 | AlivePDF. Создание pdf-документов из flash » |
Графики и диаграммы на веб-страницах. Часть 4
Эта статья завершит собой рассказ о методиках внедрения в html-страницы графиков, диаграмм, а также рассказ о javascript библиотеках, умеющих “рисовать” красивые и интерактивные изображения таких структур данных как графы и деревья. В прошлой статье я начал рассказ об одной из самых лучших библиотек подобного плана – jit.Первый пример, который мы сделали с помощью jit, демонстрировал методику построения Hypertree. Т.е. дерева, размер и расположение узлов которого упорядочены специально таким образом, чтобы акцентировать внимание пользователя на центральном узле дерева или на том узле, который был выделен пользователем. Несмотря на свое название HyperTree умеет отображать еще и “настоящие” графы, а не только деревья. Сегодня я расскажу об оставшихся в “арсенале” JIT компонентах: RGraph, SpaceTree и TreeMap. Но прежде чем мы перейдем к рассмотрению “красивостей”, я хотел бы уделить пару слов классу TreeUtil. Из названия ясно, что класс носят вспомогательный характер и используются для выполнения неких “вычислительных работ” над данными, образующими дерево. И действительно, вот, к примеру, набор методов, которые входят в состав класса TreeUtil: prune, getParent, getSubtree, getLeaves, eachLevel, each, loadSubtrees. Чтобы понять то, какую работу выполняет первый метод “prune” нужно вспомнить определение уровня, на котором находится каждый из узлов дерева. Предположим, что у нас есть следующая иерархия: в качестве корня дерева будет узел “food”, из которого отходят две ветви “fruits” и “vegetables”, из которых, в свою очередь, произрастают уже конкретные наименования фруктов и овощей, например, “apples”, “oranges”, “potato”. Так вот корневой узел имеет номер уровня равный нулю, затем, узлы “oranges” и “vegetables” имеют уровень равный единице. Уровень номер два соответствует узлам дерева с названиями овощей и фруктов. Метод prune принимает на вход два параметра: массив с данными для дерева и число. Число – это номер уровня, после которого все узлы дерева будут удалены. В результате выполнения следующего кода из набора данных будут удалены все узлы, расположенные на уровне 2 и ниже (т.е. конкретные названия фруктов):
var data =
{id: 'food', name: 'Food’,
children : [
{id: 'fruits', name: 'Fruits',
children: [
{id: 'apples', name: 'Apples', children: []},
{id: 'oranges', name: 'Oranges', children: []}
]}
]
};
TreeUtil.prune (data, 1);
TreeUtil.eachLevel(tree, 0, 2, function(node, level) {
alert(‘id = ’ + node.id + ' level = ' + level);
});
// и второй пример
TreeUtil.each(tree, function(node) {
alert(‘id = ’ + node.id);
});
Давайте начнем с небольшого примера построения SpaceTree и разберем его по шагам. Первый шаг – это, конечно, подготовка данных. Данные для SpaceTree ничем не отличаются от данных, которые были использованы для показанного в прошлой статье примера с HyperTree:
var data =
{
id: 'organization', name: 'Organization', children : [
{id: 'managers', name: 'Managers', children: [
{id: "jim", name: "Jim Tapkin", children: []},
]},
{id: 'security', name: 'Security', children: [
{id: "ivan", name: "Ivan Dolvich", children: []},
{id: "igor", name: "Igor Dolvich", children: []},
]},
{id: 'planning', name: 'Planning', children: [
{id: "tim", name: "Tim Timur", children: []},
{id: "chen", name: "Li Chen", children: []},
]}
]
};
var canvas = new Canvas('placeholder', {
'injectInto': 'placeholder',
'width': 1024,
'height': 768
}
);
Для того, чтобы понять на что влияет параметр levelDistance внимательно рассмотрите рис. 1.
Если на рисунке отображается сразу много узлов, то нужен тонкий контроль за расстоянием между узлами дерева, так чтобы они не наползали друг на друга. Для настройки расстояния по горизонтали между узлом и его непосредственными дочерними узлами используется levelDistance. Для управления расстоянием между узлами деревам по вертикали используется параметр siblingOffset. Еще из полезных, хоть и не показанных в примере настроек, есть levelsToShow. Согласитесь, что если дерево большое, то показать сразу все узлы невозможно. Итак, levelsToShow (по-умолчанию равное двум) управляет тем, сколько уровней дерева будет показано на рисунке одновременно. В том случае, если вы не хотите отобразить на рисунке подписи к узлам дерева, то можно указать параметр “withLabels” равным false. Остальные настройки управляет тем, как будут выглядеть узлы дерева (свойства, объединенные под названием Node) и соединяющие их дуги (Edge). Внешний вид узла дерева (форма) задается параметром type, который принимает значения circle, rectangle, ellipse, square. Также можно отключить отображение узла, если задать для type значение равное none. Размеры узла определяются совокупностью значений width, height и dim (радиус круга). Фоновый цвет и толщина линий, которыми “рисуется” узел дерева, зависят от переменных lineWidth и color. Последний параметр overridable будучи включенным позволяет задавать для каждого из узлов дерева индивидуальный внешний вид. Как это сделать можно легко найти в примерах, размещенных на официальном сайте jit (http://thejit.org). Что касается настроек внешнего вида линий, соединяющих узлы дерева, то я сделал их красного цвета в пять пикселей толщиной. Параметр же type служит для управления типом линии и принимает значения "none", "line", "quadratic:begin", "quadratic:end", "bezier" и "arrow". Затем для каждого из узлов дерева я захотел расположить рядышком текстовую надпись. Настройка внешнего вида надписи делегируется функции обрабатывающему событие “onCreateLabel”. Здесь, моя функция должна назначить для подписи стилевое оформление (css-класс ‘labelClass’), а также сам текст подписи. Для того, чтобы рисунок SpaceTree был интерактивным я назначил каждой из надписей функцию “ловящую” click пользователя по надписи и выполняющую центрирование дерева вокруг выделенного пользователем узла дерева. После клика на любом из узлов SpaceTree пересчитает новое расположение узлов дерева, так чтобы выбранный пользователем узел находился в центре экрана и были видны только соседние с ним узлы.
var st = new ST(canvas, {
duration: 800,
transition: Trans.Quart.easeInOut,
orientation: 'top',
levelDistance: 150,
Node: {
width: 100, height: 100, dim: 20,
type: 'circle',
color: '#00ff00',
overridable: false
},
Edge: {
type: 'bezier',
overridable: false,
lineWidth: 5,
color: '#ff0000'
},
onCreateLabel: function(label, node){
label.id = node.id;
label.innerHTML = node.name;
label.onclick = function(){
st.onClick(node.id);
};
var style = label.className="labelClass";
}
}
);
st.loadJSON(data);
st.compute();
st.onClick(st.root);
На этом про компонент SpaceTree все, а мы переходим к рассмотрению следующего средства визуализации – Rgraph. Сразу скажу: то, что у нас должно получиться, показано на рис. 3.
Как видите, эта структура данных отлично подходит для радиальных деревьев, т.е. деревьев, узлы которых расположены вдоль концентрических окружностей с центром, совпадающим с расположением корневого элемента. Для следующего примера я решил воспользоваться набором данных из предыдущего примера, а вот конструирование canvas или “холста” для рисования уже имеет небольшие особенности.
var canvas = new Canvas('placeholder ', {
injectInto: 'placeholder',
width:800,
height: 800,
backgroundCanvas: {
styles: {
strokeStyle: 'red', lineWidth: 2
},
impl: {
init: function(){},
plot: function(canvas, ctx){
var times = 3, d = 100;
var pi2 = Math.PI * 2;
for (var i = 1; i <= times; i++) {
ctx.beginPath();
ctx.arc(0, 0, i * d, 0, pi2, true);
ctx.stroke();
ctx.closePath();
}
}
}
}
}
);
var rgraph = new RGraph(canvas, {
Node: {
color: 'black',
width: 15, height: 15, type: 'rectangle'
},
Edge: {
color: 'black'
},
onCreateLabel: function(elt, node){
elt.innerHTML = node.name;
elt.onclick = function(){
rgraph.onClick(node.id);
};
},
onPlaceLabel: function(domElement, node){
var style = domElement.style;
style.display = '';
style.cursor = 'pointer';
if (node._depth <= 1) {
style.fontSize = "1.4em";
style.color = "black";
}
else if(node._depth == 2){
style.fontSize = "1.7em";
style.color = "black";
} else
style.display = 'none';
var left = parseInt(style.left);
var w = domElement.offsetWidth;
style.left = (left - w / 2) + 'px';
}
}
);
rgraph.loadJSON(data);
rgraph.refresh();
Эта структура данных идеально подходит для того, чтобы в ограниченном пространстве одновременно отобразить сведения о большом количестве элементов связанных отношениями подчинения. Так, если какой-то узел дерева является дочерним по отношению к другому узлу, то и выражается это в том, что в прямоугольник родительского узла будут помещены все дочерние узлы. Надо сказать, что TreeMap стоит особняком от рассмотренных ранее HyperTree, SpaceTree и RGraph, т.к. для того, чтобы нарисовать диаграмму TreeMap не использует canvas, а формирует множество тегов div, многократно вложенных друг в друга. Честно говоря, я в своей практике я ни разу не сталкивался с необходимостью использования TreeMap. Т.к. за счет “игры” с настройками других компонентов jit можно добиться отображения одновременно желаемого количества компонентов на экране. A если вам хочется показать информацию именно в таком стиле как это делает TreeMap, то это проще сделать самому “руками”, сформировав набор вложенных друг в друга блоков div. Плюс в том, что вы получаете полный контроль за расположением элементов, наличием между ними отступов. Так при использовании TreeMap тяжело определить действительную последовательность вложенных узлов-элементов, если количество уровней больше трех.
Естественно, что доступных библиотек, позволяющих внедрять в html-страницы графики, диаграммы, изображения деревьев и графов, очень много. И зачастую они содержат набор функций более совершенный, чем описанные мною flot, moowheel, jit. Как оправдание я могу сказать, что рассмотренный мною набор библиотек покрывают 90% потребностей начинающего javascript-разработчика и при всем этом остаются простыми в использовании.
« Графики и диаграммы на веб-страницах. Часть 3 | AlivePDF. Создание pdf-документов из flash » |