| Сегодня Воскресенье 22.12.2024

Создаем граф при помощи jQuery + jsPlumb


Задача: найти способ визуализации объектов (элементов HTML-верстки) в виде графа.

Перерыв на просторах интернета достаточно большое количество готовых инструментов для рисования векторной графики при помощи Javascript, я нашел оптимальное, для меня решение. Это использование jQuery вместе с плагином jsPlumb. Этот плагин использует возможности тега canvas для рисования графики. Все современные браузеры его уже поддерживают (хотя и не в полном объеме), за исключением, как вы уже догадались, Internet Exlorer (включая 8-ю версию). Благо команда Google своевременно позаботилась о портировании большинства возможностей canvas через поддержку VML (которую только и имеет IE). В версии гугла нету поддержки градиентов. Для придания красоты и лучшего визуального восприятия, я воспользовался библиотекой Сергея Чикуенка rocon для скругления уголков HTML-блоков.

Плагин jsPlumb имеет всего лишь 2 вида конечных точек (Endpoint) при соединении блоков. Это квадрат и круг. Но для для графа важно отражать направление связей между объектами, для чего идеально бы подходили стрелки (треугольники). Поэтому я немного модифицировал плагин и добавил новый метод для рисования треугольника (модифицированный плагин jquery.jsPlumb-1.0.4.mod.js), желающие его могут найти и изменить под свои нужды (например сделать просто стрелки вместо залитого треугольника). При запуске плагина jsPlumb, он получает идентификатор (id) HTML-элемента и используя встроенные или переданные настройки, создает 3 элемента canvas. Два из них это элементы Endpoint, а третий — соединительная линия. Для создания нашего графа мы будем скрывать начальную точку чтобы она не мешала восприятию направления в связях объектов. Чтобы создать древовидную структуру таких объектов, я сделал специальную плавающую верстку, которая имеет специальные родительские контейнеры, которые могут содержать как еще одну "ветку" графов, так и непосредственно сами конечные элементы графов. Далее, вся эта конструкции парсится при помощи jQuery, плагином jsPlumb создаются связи между объектами. Но что делать, если один объект может иметь сколько угодно "входов" и "выходов"? Для этого каждый конечный элемент графа имеет дополнительный HTML-аттрибут "also-connection-with" который содержит список дополнительных элементов, с которыми надо построить связь. Эти элементы также парсятся и по аналогии строятся связи к ними.

Вот что получилось в итоге:

<html>
 <head>
 <title>jsPlumb demo</title>
 <script type="text/javascript" src="http://explorercanvas.googlecode.com/svn/trunk/excanvas.js"></script>
 <script type="text/javascript" src="http://rocon.googlecode.com/svn/trunk/js/rounded-corners-min.js"></script>
 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
 <script type="text/javascript" src="jquery.jsPlumb-1.0.4.mod.js"></script>
 <script type="text/javascript">
 $(function()
 {
 // выставляем параметры по умолчанию
 jsPlumb.setDraggableByDefault( false );
 jsPlumb.DEFAULT_PAINT_STYLE = { strokeStyle: 'gray', gradient:{ stops:[[0,'#225588'], [1,'#558822']] }, lineWidth: 1 };
 jsPlumb.DEFAULT_ANCHORS = [ jsPlumb.Anchors.RightMiddle, jsPlumb.Anchors.LeftMiddle ];
 jsPlumb.DEFAULT_ENDPOINT_STYLES = [{ fillStyle:'#225588' }, { fillStyle:'#558822' }]; 
 jsPlumb.DEFAULT_ENDPOINT = new jsPlumb.Endpoints.Triangle({width:15, height:15});
 jsPlumb.DEFAULT_CONNECTOR = new jsPlumb.Connectors.Bezier();

 var jsPlumbAnchorsRef = {
 top : jsPlumb.Anchors.TopCenter,
 right : jsPlumb.Anchors.RightMiddle,
 bottom : jsPlumb.Anchors.BottomCenter,
 left : jsPlumb.Anchors.LeftMiddle
 }
 
 // парсим иерархию объектов в верстке и строим связи
 $('#plumbs div.window + div.tasks-box').each(function()
 {
 var level = $(this).prev();
 $(this).find('> div.window').each(function(){ level.plumb({ target: this.id }) });
 });
 
 // парсим дополнительные связи между объектами
 $('#plumbs div.window').each(function()
 {
 var self = $(this),
 connections = self.attr('also-connection-with');
 
 if( connections != undefined )
 {
 $(connections.split(/\s*;\s*/)).each(function()
 {
 var connectWith = this.split(/\s*:\s*/);
 var id = connectWith[0];
 var anchor = connectWith[1].split('-');
 
 self.plumb({ target: id, anchors: [jsPlumbAnchorsRef[anchor[0]], jsPlumbAnchorsRef[anchor[1]]] });
 });
 }
 });
 
 // скрываем стартовые точки в каждой связе
 $('canvas._jsPlumb_endpoint').filter(':even').hide();
 
 });
 </script>

 <style type="text/css"> 
 ._jsPlumb_connector { z-index:100; }
 .rocon { z-index:10; }
 
 .tasks-box { float:left; }
 .tasks-box h4 { margin:0; padding-bottom:.5em; }
 .tasks-box .window { 
 position:relative; 
 border:1px solid #225588;
 background:white;
 padding:1em; 
 margin:1.5em 3.5em;
 float:left; 
 clear:left;
 z-index:10;
 }
 .tasks-box a { color:#039; text-decoration:none; border-bottom:2px solid #039; }
 .tasks-box a:hover { border-bottom:2px dotted gray; color:gray; }
 </style>
 
 </head>
 <body>
 
 <!-- sample tasks tree -->
 <div id="plumbs" class="tasks-box">
 <div class="window rc15" id="window1" also-connection-with="window6:bottom-top">
 <h4>Task name 1</h4>
 <em>Description</em>
 </div>
 <div class="window rc15" id="window2">
 <h4>Task name 2</h4>
 <em>Description</em>
 </div>
 <div class="tasks-box">
 <div class="window rc15" id="window4">
 <h4>Task name 4</h4>
 <em>Description</em>
 </div>
 <div class="window rc15" id="window5">
 <h4>Task name 5</h4>
 <em>Description dsfa dsfdsaf das fdsaf dsf dasf sdfdsf</em>
 </div>
 <div class="window rc15" id="window6">
 <h4>Task name 6</h4>
 <em>Description</em>
 </div>
 <div class="tasks-box">
 <div class="window rc15" id="window8" also-connection-with="window4:right-right; window5:right-right; window6:bottom-bottom">
 <h4>Task name 8</h4>
 <em>Description dsf adsf adsfdasf df</em>
 </div>
 </div>
 </div>
 <div class="window rc15" id="window3">Text block 3</div>
 <div class="window rc15" id="window7"><a href="http://morrisonpitt.com/jsPlumb/doc/usage.html" title="Documentation">jsPlumb docs</a></div>
 </div>
 <br clear="all" />
 <!-- /sample tasks tree -->
 
 </body>
</html> 
Категория: Разное | Добавил: Admin (10.10.2010)
Просмотров: 8290 | Рейтинг: 5.0/1
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]