Итак. Описанный ниже пример создаёт средствами Raphael и PHP круговую диаграмму в формате SVG, представленную на изображении. Мы постарались максимально подробно описать исходный код, чтобы помочь всем тем, кто самостоятельно разобраться в этом не нашёл сил или времени.
Файл circle.php:
<?php // для начала необходимо создать объект с определённым идентификатором, // заданной ширины и высоты для дальнейшей работы function start_paper($id, $width, $height) { return 'var r = Raphael("' .$id .'", ' .$width .', ' .$height .');'; } // создание круговой диаграммы function paper_circle_chart($params) { // расчёт радиуса "излома" как половины толщины сектора // для создания 3D эффекта выпуклости диаграммы $shadow_width = floor(($params['radius']-$params['inradius'])*0.47); $pradius = $params['radius'] - $shadow_width; // радиус выноса линий выноски $outradius = $params['radius'] + $params['text_radius']; // сумма данных $total = array_sum($params['data']); $i = 0; $prev = 0; $fangel = 0; $code = ''; $pcode = ''; $pline = ''; $ptext = ''; $pstext = ''; $center = ''; $begin = true; // рисуем секторы диаграммы в цикле foreach ($params['data'] as $k => $v) { // определяем цвет текущего сектора $color = $params['colors'][$i]; // если нулевой сектор проходим мимо if ($v == 0) { $i++; continue; } // если диаграмма состоит из одного сектора - рисуем сплошной круг elseif ($v == $total) { $pend = deg2rad(45); $code = ' r.circle(' .$params['centerx'] .', ' .$params['centery'] .', ' .$params['radius'] .').attr({"fill": "' .$color .'"})'; if (count($params['texts'])) { $dxc = round($params['centerx'] + $pradius * sin($pend), 2); $dyc = round($params['centery'] - $pradius * cos($pend), 2); $dxc1 = round($params['centerx'] + $outradius * sin($pend), 2); $dyc1 = round($params['centery'] - $outradius * cos($pend), 2); $dxc2 = $dxc1 + $params['text_width']; $dxc3 = $dxc1 + round($params['text_width']/2, 2); $dyc2 = $dyc1 - $params['text_minus']; $dyc3 = $dyc1 + $params['text_plus']; $pcode .= ' r.circle(' .$dxc .', ' .$dyc .', ' .$params['point_radius'] .')'; $pline .= ' r.path("M'. $dxc .','. $dyc .' L'. $dxc1 .','. $dyc1 .' L'. $dxc2 .','. $dyc1 .'")'; $ptext .= ' r.text(' .$dxc3 .', ' .$dyc2 .', "100 %")'; $pstext .= ' r.text(' .$dxc3 .', ' .$dyc3 .', "' .$params['texts'][$i] .'")'; } } // иначе - рисуем текущий сектор else { $percent = $v / $total; $angel = 360 * $percent; $rad = deg2rad($angel); $end = $prev + $rad; $pend = $prev + $rad/2; $dx = round($params['centerx'] + $params['radius'] * sin($prev), 2); $dy = round($params['centery'] - $params['radius'] * cos($prev), 2); $dxp = round($params['centerx'] + $params['radius'] * sin($end), 2); $dyp = round($params['centery'] - $params['radius'] * cos($end), 2); if ($percent > 0.5) $sec = 1; else $sec = 0; if (!$begin) $code .= ','; $code .= ' r.path("M' .$params['centerx'] .',' .$params['centery'] .' L' .$dx .',' .$dy .' A' .$params['radius'] .',' .$params['radius'] .' 0 ' .$sec .',1 ' .$dxp .',' .$dyp .' z").attr({"fill": "' .$color .'"})'; if (count($params['texts'])) { $dxc = round($params['centerx'] + $pradius * sin($pend), 2); $dyc = round($params['centery'] - $pradius * cos($pend), 2); $dxc1 = round($params['centerx'] + $outradius * sin($pend), 2); $dyc1 = round($params['centery'] - $outradius * cos($pend), 2); if (($fangel + $angel/2) > 180) { $dxc2 = $dxc1 - $params['text_width']; $dxc3 = $dxc1 - round($params['text_width']/2, 2); } else { $dxc2 = $dxc1 + $params['text_width']; $dxc3 = $dxc1 + round($params['text_width']/2, 2); } $dyc2 = $dyc1 - $params['text_minus']; $dyc3 = $dyc1 + $params['text_plus']; if (!$begin) { $pcode .= ','; $ptext .= ','; $pstext .= ','; $pline .= ','; } $pcode .= ' r.circle(' .$dxc .', ' .$dyc .', ' .$params['point_radius'] .')'; $pline .= ' r.path("M'. $dxc .','. $dyc .' L'. $dxc1 .','. $dyc1 .' L'. $dxc2 .','. $dyc1 .'")'; $ptext .= ' r.text(' .$dxc3 .', ' .$dyc2 .', "' .round($percent * 100) .' %")'; $pstext .= ' r.text(' .$dxc3 .', ' .$dyc3 .', "' .$params['texts'][$i] .'")'; } $i++; $begin = false; $prev = $end; $fangel += $angel; } } // устанавливаем атрибут "контур" для всех секторов диагрммы if ($code) $code = ' var st = r.set(); st.push(' .$code .' ); st.attr({"stroke": "none"});'; // устанавливаем атрибут "цвет" для всех линий выноски if ($pline) $pline = ' var st = r.set(); st.push(' .$pline .' ); st.attr({"stroke": "' .$params['line_color'] .'"});'; // устанавливаем атрибуты "контур" и "цвет заливки" для плашек всех линий выноски if ($pcode) $pcode = ' var st = r.set(); st.push(' .$pcode .' ); st.attr({"fill": "' .$params['text_color'] .'", "stroke": "' .$params['stroke_color'] .'"});'; // устанавливаем атрибуты "шрифт", "размер" и "цвет" для подписей над линией выноски if ($ptext) $ptext = ' var st = r.set(); st.push(' .$ptext .' ); st.attr({"font-family": "' .$params['font'] .'", "font-size": "' .$params['text_name'] .'", "fill": "' .$params['text_color'] .'", "cursor": "default"});'; // устанавливаем атрибуты "шрифт", "размер" и "цвет" для подписей под линией выноски if ($pstext) $pstext = ' var st = r.set(); st.push(' .$pstext .' ); st.attr({"font-family": "' .$params['font'] .'", "font-size": "' .$params['text_small'] .'", "fill": "' .$params['text_color'] .'", "cursor": "default"});'; // создаём центр диаграммы и текст внутри него $inradius = ''; if ($params['inradius'] > 0) { $inradius = ' r.circle(' .$params['centerx'] .', ' .$params['centery'] .', ' .$params['inradius'] .').attr({"fill": "' .$params['center_text_back'] .'", "stroke": "none"});'; if ($params['shadow']) $inradius .= ' r.circle(' .$params['centerx'] .', ' .$params['centery'] .', ' .($params['radius']-$shadow_width+floor($shadow_width/2)) .').attr({"fill": "none", "stroke": "#FFFFFF", "stroke-width": "' .$shadow_width .'", "stroke-opacity": "0.15"});'; if ($params['center_text']) { $center = ' r.text(' .$params['centerx'] .', ' .$params['centery'] .', "' .$params['center_text'] .'").attr({"font-family": "' .$params['font'] .'", "font-size": "' .$params['center_text_size'] .'", "fill": "' .$params['center_text_color'] .'", "cursor": "default"});'; } } return $code .$inradius .$pline .$pcode .$ptext .$pstext .$center; } ?>
Файл test.php:
<?php include("circle.php"); $params = array ( // параметры диаграммы 'font' => 'PT Sans, Tahoma', // шрифт 'text_color' => '#212121', // цвет текста 'line_color' => '#494949', // цвет линий выноски 'text_width' => 40, // ширина горизотальной части линий выноски 'text_radius' => 14, // расстояние отступа горизотальной части линий выноски от диаграммы 'point_radius' => 3, // радиус плашки линии выноски ( круг в месте соединения линии выноски с сектором данных ) 'stroke_color' => '#EFEFEF', // цвет контура плашки 'text_name' => 13, // размер текста над линией выноски 'text_small' => 11, // размер текста под линией выноски 'text_minus' => 10, // отступ текста вверх над линией выноски 'text_plus' => 8, // отступ текста вниз под линией выноски 'centerx' => 100, // центр диаграммы по оси x 'centery' => 100, // центр диаграммы по оси y 'radius' => 42, // внешний радиус диаграммы 'inradius' => 19, // радиус внутреннего круга 'center_text_back' => '90-#e7e7e7-#ffffff:60', // цвет внутреннего круга 'center_text' => 416, // надпись в центре внутреннего круга 'center_text_size' => 14, // размер текста в центре внутреннего круга 'center_text_color' => '#212121', // цвет текста в центре внутреннего круга 'data' => array( // исходные данные 139, 112, 89, 76), 'texts' => array ( // подписи к секторам, отображаются под линией выноски 'пас', 'навес', 'в разрез', 'прострел' ), 'colors' => array ( // цвета секторов '0-#08b2ff-#0e56d4', '0-#fffa17-#ffba17', '0-#e0070e-#f15722', '0-#BCE408-#5FBB00' ), 'shadow' => 1 // делать или нет 3D эффект выпуклости диаграммы ); // создаём диаграмму, в <head></head> размещаем js код, в <body></body> - <div></div> // c идентификатором идентичным переданному в конструктор диаграммы start_paper() $head = start_paper('diagram', 200, 200) .paper_circle_chart($params); $body = '<div id="diagram"></div>'; echo ' <!DOCTYPE html> <html> <head> <title>Пример круговой SVG диаграммы средствами Raphael и PHP</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <script src="/raphael-min.js"></script> <script> window.onload = function () {' .$head .' }; </script> </head> <body>' .$body .' </body> </html>'; ?>
Приведенный выше исходный код полностью рабочий. Каждый желающий поковыряться и разобраться в изложенном материале самостоятельно может скачать библиотеку и собрать три файла (circle.php, test.php и raphael-min.js) в единое целое.
Итоговый html, который получает клиент:
<!DOCTYPE html> <html> <head> <title>Пример круговой SVG диаграммы средствами Raphael и PHP</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <script src="/raphael-min.js"></script> <script> window.onload = function () {var r = Raphael("diagram", 200, 200); var st = r.set(); st.push( r.path("M100,100 L100,58 A42,42 0 0,1 136.27,121.18 z").attr({"fill": "0-#08b2ff-#0e56d4"}), r.path("M100,100 L136.27,121.18 A42,42 0 0,1 74.6,133.45 z").attr({"fill": "0-#fffa17-#ffba17"}), r.path("M100,100 L74.6,133.45 A42,42 0 0,1 61.7,82.76 z").attr({"fill": "0-#e0070e-#f15722"}), r.path("M100,100 L61.7,82.76 A42,42 0 0,1 100,58 z").attr({"fill": "0-#BCE408-#5FBB00"}) ); st.attr({"stroke": "none"}); r.circle(100, 100, 19).attr({"fill": "90-#e7e7e7-#ffffff:60", "stroke": "none"}); r.circle(100, 100, 37).attr({"fill": "none", "stroke": "#FFFFFF", "stroke-width": "10", "stroke-opacity": "0.15"}); var st = r.set(); st.push( r.path("M127.75,84.07 L148.57,72.12 L188.57,72.12"), r.path("M106.24,131.39 L110.93,154.92 L150.93,154.92"), r.path("M68.99,107.89 L45.73,113.81 L5.73,113.81"), r.path("M82.63,73.13 L69.59,52.97 L29.59,52.97") ); st.attr({"stroke": "#494949"}); var st = r.set(); st.push( r.circle(127.75, 84.07, 3), r.circle(106.24, 131.39, 3), r.circle(68.99, 107.89, 3), r.circle(82.63, 73.13, 3) ); st.attr({"fill": "#212121", "stroke": "#EFEFEF"}); var st = r.set(); st.push( r.text(168.57, 62.12, "33 %"), r.text(130.93, 144.92, "27 %"), r.text(25.73, 103.81, "21 %"), r.text(49.59, 42.97, "18 %") ); st.attr({"font-family": "PT Sans, Tahoma", "font-size": "13", "fill": "#212121", "cursor": "default"}); var st = r.set(); st.push( r.text(168.57, 80.12, "пас"), r.text(130.93, 162.92, "навес"), r.text(25.73, 121.81, "в разрез"), r.text(49.59, 60.97, "прострел") ); st.attr({"font-family": "PT Sans, Tahoma", "font-size": "11", "fill": "#212121", "cursor": "default"}); r.text(100, 100, "416").attr({"font-family": "PT Sans, Tahoma", "font-size": "14", "fill": "#212121", "cursor": "default"}); }; </script> </head> <body><div id="diagram"></div> </body> </html>
О том, как работает градиентная заливка (0-#BCE408-#5FBB00) с точки зрения синтаксиса Raphael и другие моменты касательно функций этой библиотеки и их параметров достаточно подробно изложены в документации. К слову подробная документация, широкий функционал и кроссбраузерность этого решения — являются, с нашей точки зрения, неоспоримым преимуществом данной библиотеки над аналогичными средствами.
Надеемся, что наш опыт будет полезен всем тем, кто впервые столкнулся с задачей построения векторной графики в html средствами JS. Постараемся ответить на все вопросы и комментарии по данной теме.
ссылка на оригинал статьи http://habrahabr.ru/post/180659/
Добавить комментарий