SVG (
scalable vector graphics) это векторный формат графики подобно EPS, анимации и интерактива с пользователем, разрабатываемый в W3C. Внутри файл не бинарный а обычный XML, описывающий объекты, их эффекты и поведение. Векторная графика в общем нужна при изменении размера изображения без потери качества, например в полиграфии. В web я это вижу в резиновых сайтах, где размеры блоков установлены в %, размер шрифтов в em, а лого — в SVG. Пересели на более хороший монитор — всё изменилось пропорционально. Практические примеры —
иконки, графики, карты, логотипы, интерфейсы. Ниже я привожу пример такого Pie-chart'а.
Интеграция
HTML — Inline
Поскольку SVG по сути XML, то его можно сразу inline-стилем описывать в XHTML-структуре. Однако как я уже убедился, XHTML1.1 doctype подразумевает что MIME документа уже не text-plain. А «ослик» IE6 не понимает XHTML в принципе, с другой стороны Firefox использует два парсера, и если MIME не application/xhtml+xml, то inline SVG не будет распознан. Это палка с двумя концами — IE и FF.
/>
Второй способ и наиболее практичный — ссылаться на существующий файл через object-тэг. Для
уменьшения траффика, файл можно сжать и получить SVGZ архив.
CSS и Javascipt
Как вы увидите ниже — поскольку SVG очень тесно связан с HTML/XML, то естественно что на графические объекты можно не только описывать при помощи CSS, но и писать Javascript-функции на всевозможные onclick и тп. event'ы (отсюда и интерактивность).
/>
Конвертирование и редакторы
Конвертировать SVG можно и в png/jpeg, но для этого фактически надо проделать работу обработчика. В PHP этим занимается
PEAR XML_svg2image библиотека. Ещё есть
сервис по конвертированию растрового изображения в векторный (правда там EPS). Из редакторов — есть
Inkscape и Adobe Illustrator, Corel Draw.
Графика
Примитивы
Всякое рисование начинается с определения пространства (двумерного), разделения на координаты (в единицах с плавающей точкой или процентах) и введения примитивных конструкций:
- line — прямая линия. x1, y1, x2, y2 — координаты
- polyline — ломанная линия. points — перечисление координат точек
- rect — прямоугольник. x,y, width, height, rx, ry — верхний левый угол, размеры, радиусы углов
- polygone — многоугольник. Похож на polyline. points — координаты точек
- circle — круг. cx, cy, r — координаты центра и радиус
- ellipse. cx, cy, rx, ry — координаты центра и радиусы
- text — текстовая надпись. Очень неудобная, потому что без переноса строк и с абсолютным позиционированием. x,y, font-family, font-size
- tspan — может описывать слова внутри text-элемента. Например сдвигать или раскрашивать
- tref — повторное использование text-элемента с указанным id
Простой текст
<tref xlink:href="#myText" x="50" y="50">
textPath — текст на path-кривой, связанный по id
/>
<textPath xlink:href="#myPath" startOffset="50%">Водка Vinogradoff
image — растровое изображение
<image image-rendering="optimizeSpeed" xlink:href"background.jpg" width="100%" height="100%" preserveAspectRatio="xMidYMid slice" filter="url(#blurpane)"/>
Параметры и стили
Как и в html, одни линии мало кому нужны — их надо закрашивать, указывать цвета, и всё это делается параметрами
- fill, fill-rule — заливка. Например «none»,blue, indigo.
- stroke, stroke-linecap, stroke-linejoin, stroke-dasharray, stroke-dashoffset, stroke-width — граница, аналог border. Вместе stroke-width.
- font-family, font-size, font-style, font-weight — шрифты для text-элементов
- text-anchor
Для заливки используются тэги:
- pattern
- linearGradient
- radialGradient
Эти параметры можно все объединить в один аналог CSS и записать в inline-стиле:
style="stroke-width:1; stroke:blue; fill:none"
Группы и кривые Безье
Элементы можно и нужно группировать друг в друга. Кроме того что
tspan устанавливаетсявнутрь
text-элемента, группирование происходи благодаря элементу.Кривые Безье это плавные переходные линии, задающиеся по точкам. В SVG для этого существует элемент path, у которого подобно ломанной линии указываются координаты. Рядом с координатами могут стоять буквы, обозначающие свойства линии. Большие буквы говорят об абсолютном позиционировании, маленькие об относительном
- M — начало кривой (x,y)
- Z — конец кривой (без координат)
- L -прямая линия (x,y)
- H — горизонтальная линия
- V — вертикальная линия
- Q — квадратичная кривая по одной точке
- T — продолжение кривой с отражением предыдущей точки — упрощает рисование повторяющихся ритмов
- С — собственно кривая Безье третьего порядка по двум точкам
- S — упрощённая версия C
- A — эллиптическая кривая (радиусы, поворот)
Трансформации и возможности
Объекты в SVG пожно искажать, крутить и перемещать при помощи фильтров, которые указываются в качестве параметров:
- translate — перенос объекта
- rotate -вращение
- scale — масштабирование
- scewX, scewY — искажение
- matrix — смешанная трансформация
SVG поддерживает фильтры с эффектами освещения. А кроме статичных изображени есть возможность, анимации и интерактивности с пользователем. Например
тетрис или нашумевший Microsoft Table и Silverlight реализован
программно в SVG, причём при большом желании — заработало и видео (формата
Ogg Theora=SVGT). Конечно последние возможности ещё не реализованы во всех браузерах, но
первооткрыватели типа Opera имеются.
Тортовый график на SVG и PHP/DB
Несомненно
Google API — очень удобная штука. Но не стоит забывать что всё-таки это внешний сервис, пусть и надёжный. Коммерческие разработки не любят рисковать, поэтому генерация графиков всё-таки должно быть локальным (если они не статические).Генерировать изображение я буду с помощью php, на основе полученных из
БД данных для отображения результатов опроса (poll). Поскольку GD-библиотека хоть и понимает размер SWF,
генерирует только бинарные файлы. Поэтому генерировать прийдётся XML:
header("Content-Type: image/svg+xml");
Посмотрим к чему надо стремится (пока без
крутой анимации)…
Начнём с резинового расположения двух эллипсов? Сектора видимо идут в порядке убывания по часовой стрелке, что-бы уменьшить количество градиента на мелких участках. Цвет сектора видимо расчитывается пропорционально числу кусочков — тут то и возникает проблема. Как оказывается, сектор нельзя просто-так залить с двумя линиями и существующим эллипсом — фигура должна быть точно очерчена, поэтому надо
рисовать всё по отдельности при помощи
path, а
ellipse нам не понадобится.
Тригонометрия цвета
Для вычисления в цикле координат path-элемента необходимо вспомнить немного математики. Поскольку
окружность — частный случай эллипса то в формулах есть много схожего, что нам очень пригодится:
x=cos(angle)*radius; y=sin(angle)*radius; //circle
x=cos(angle)*rx; y=sin(angle)*ry; //ellipse
Для эллипса же радиус просто заменяется на большую и малую полуось соответсвенно. Дальше создаём цикл, где вычисляем процентное распределение данных, градус длясектора с
path. Вчисляем координаты для арки, для этого используем «сдвиг градуса» что-бы отрисовка всех секторов шла вплотную.RGB куб, можно по разному резать, делать проекции и вводить свои системы
координат типа CMYK и HSL. Разные оттенки выбранной оранжевой заливки получаются при разбиении отрезка между выбранной точкой-цветом и вершиной белого цвета (255,255,255).
if ($intTotalValue) // если сумма значений голосования больше нуля то можно делить и рисовать
foreach((array) $Data as $key=>$recEntry){
$Data[$key]->percent=$recEntry->value/$intTotalValue;
$Data[$key]->color[0]=round($graph->fill[0]+($key/count($Data)*(255-$graph->fill[0])));
$Data[$key]->color[1]=round($graph->fill[1]+($key/count($Data)*(255-$graph->fill[1])));
$Data[$key]->color[2]=round($graph->fill[2]+($key/count($Data)*(255-$graph->fill[2])));
$Data[$key]->degree=360*$Data[$key]->percent;
$Data[$key]->start['x']=$graph->cx+round(cos(deg2rad($intDegreeShift)) * $graph->rx,3);
$Data[$key]->start['y']=$graph->cy+round(sin(deg2rad($intDegreeShift)) * $graph->ry,3);
$Data[$key]->end['x']=$graph->cx+round(cos(deg2rad($intDegreeShift+$Data[$key]->degree)) * $graph->rx,3);
$Data[$key]->end['y']=$graph->cy+round(sin(deg2rad($intDegreeShift+$Data[$key]->degree)) * $graph->ry,3);
$intDegreeShift+=$Data[$key]->degree; //increase degree shift
$boolIsLargeArc=$Data[$key]->degrees>180? 1 : 0;
echo "\n".'/>';
}
Если посмотреть на график внимательно, то видно что объёмность делается
при помощи градиента, который в SVG мы сначала зальём в качестве фона,
а поверх будем накладывать уже конкретные цвета сектора с прозрачностью:
/>
/>
/>
Основные проблемы
Но проблема остаётся и с закрашиванием пограничных секторов — закрашивать переднюю область поверх градиента надо только у тех областей, которые до половины торта идут. К этому этапу картинка выглядит так:
Абсолютный текст
Кроме того вопрос возникает как располагать текстовые описания секторов? Google попросту ведёт линии с середин секторов, даже если текст не умещается в одну строчку. В SVG позиционировать текст надо от верхнего левого угла — что уже проблема, поскольку ширины мы не знаем. Выхода два — отказаться от относительного позиционирования текста и сделать выноски цветом, либо использовать текст с фиксированной шириной (monospace, Courier) и на этой основе
расчитывать длину в пикселях и положение на лету. Благо я
наткнулся на параметр
text-anchor:end, который странным образом развернул текст как надо.
Интерактивность и будущее
Благодаря интеграции с javascript'ом, можно соответсвенно в реальном времени при помощи AJAX'а
обновлять изображение. В моём случае это необходимо когда пользователь голосует и надо обновить распределение голосов на графике.
Читайте также:
Оригинал статьи с иллюстрациями и
пример использования